var questEvent = require('./questEvent.js');

var countingTaskTypes = {
    'ArrangeTeam':1,
    'Business':1,
    'EnrollRole':1,
    'ExchangeMaterial':1,
    'FightPVE':1,
    'FightPVETo':1,
    'FightRank':1,
    'FightTower':1,
    'GetLevelReward':1,
    'HarvestFood':1,
    'HarvestGold':1,
    'KillGeneral':1,
    'MakeEquip':1,
    'MakeWeaponTo':1,
    'MakeArmorTo':1,
    'MakeAccessoryTo':1,
    'OccupyMine':1,
    'RadePVE':1,
    'RefineRole':1,
    'StrengthEquip':1,
    'TrainRole':1,
    'UpgradeGrouth':1,
    'UpgradeSkill':1,
    'UpgradeSoldier':1,
    'UpgradeTech':1,
    'WearEquip':1,
    'WinPVE':1,
    'UpgradeSoulLevel':1,
    'OpenGem':1,
};

function Player(uid) {
    this.uid = uid;
    this.openid = 'unknown';

    this.user = null;
    this.dirty = {};                    // 本次操作脏数据
    this.allDirty = {};                 // 累计的脏数据
    this.saveCount = 0;                 // 数据库存盘计数
    this.isSaving = false;              // 是否正在保存
    this.saveError = false;             // 是否出现数据库写入错误
    this.lastActive = common.getTime(); // 上次活跃时间
    this.roleDirty = true;              // 阵上武将数据是否需要重新计算
    this.lock = false;                  // 并发锁
    this.battleWorldSaveTeam= true;     // 决战天下是否需要保存阵型数据 

    this.doingTasks = [];               // 当前操作带来的任务更新
    this.finishedAchievements = [];     // 新完成的成就列表

    this.cloudMessages = [];            // 云服务器消息
    this.inviteGoal = {};               // 邀请情况

    this.roleAttrs = {};                // 武将属性计算

    this.hackVerifyKey = null;          // Hack验证key

    this.open_session_key = null;

    this.loginStamp = 0;                // 登录时间戳

    this.quarterActionScore = 0;
    this.lastMsgQuarter = 0;
    this.action = {                     // 当前消息
        mod: 'mod',                         // 消息模块
        act: 'act',                         // 消息接口
        score: 0,                           // 消息评分
        getEvent: function() {
            return util.format('%s.%s', this.mod, this.act);
        },
    };

    this.status = {                 // 操作事项
        /*
        newRole: false,                 // 新创建角色
        awards: [],                     // 非用户主动行为获得的奖励
        notice: "",
        */
    };
}

Player.create = function(uid) {
    var initUser = {
        '_id' : uid,
        'lock' : 0,             // 并发锁, 0空闲 >0解锁时间(单位s)
        'ai' : 1,               // 自增长ID,用于编号系统
        'active_time' : 0,      // 活跃时长(lastActivie - createTime)
        'info' : {              // 基本信息
            'un':'',            // 玩家姓名
            'headpic':'',       // 玩家头像
            'version':'',       // 上次登录客户端版本
            'yellow':0,         // 黄钻等级
            'blue':0,           // 蓝砖等级
            'yellow_year':0,    // 年费黄钻
            'blue_year':0,      // 年费蓝钻
            'platform':'',      // 平台 qzone,pengyou,3366
            'create': common.getTime(),  // 用户创建时间
            'via':'',           // 来源 qq_task, gdt, default
            'invited':'',       // 邀请者
            //'roll' : 0,      // 0/1 滚服标记
        },

        'status' : {            // 基础数据
            'xp':0,             // 经验
            'level':1,          // 等级
            'gold':100000,      // 银两
            'cash':0,           // 元宝
            'vip':0,            // VIP等级
            'food':8000,        // 粮草
            'honor':6000,       // 荣誉
            'soul':0,           // 将魂
            'hsoul':0,          // 星魂
            'fight_force':0,    // 战斗力
            'friend_point':0,   // 义气点数
        },

        'mark' : {              // 标志
            'tutorial':101,     // 新手引导步奏,0完成
            'day':0,            // 上次登录日期20130101
            'logins':0,         // 累计登录次数
            'bits':0,           // 标志位
            'daily_task':0,     // 每日任务最后领取分数
            'food':0,           // 每日已经付费粮食次数
            'npc_output':0,     // 每日臣民已经产出次数
            'yellow': 0,        // 是否领取每日黄钻奖励
            'league' : 0,       // 是否领取每日军团奖励
            'login_days':0,     // 累计登录天数
            'gold':0,           // 每日已经付费购买金币次数
            'p3366':0,          // 是否领取每日3366奖励(3366平台)
            'blue': 0,          // 是否领取每日蓝钻奖励
            'dragon' : 0,       // 0/1 神龙许愿标记
            'level_pack_award' : 0, // 新手等级礼包获取情况
            'rename':0,         // 是否已经改名
            'online_time':0,    // 在线总时长
            'block':0,          // 封号时间
        },

        'role' : {              // 武将 
        /*
            'rid':{             // 编号
                'id'            // 编号
                'xp'            // 经验
                'level'         // 等级
                'soldier_level' // 兵种等级
                'grouth'        // 成长值
                'weapon'        // 武器
                'armor'         // 防具
                'accessory'     // 饰品
                'skill'         // 技能
                'skill_levels:{ // 技能等级
                    'skill':level
                },
                'fight_force'   // 战斗力
                'soul'          // 练魂等级
                'god':0,        // 神将等级
                'soul_break':0  // 炼魂突破
                'grouth_break':0// 培养突破
                'ride':{        // 坐骑
                    'horse' : 0,// 系统生成坐骑ID
                    'seat' : 0, // 马鞍等级
                    'whip' : 0, // 马鞭等级
                }
                'wing' : level, // 羽翼等级
                'star':0,       // 升星等级
            }
        */
        },

        'role_card' : {         //武将卡牌
            //'rid': counter
        },

        'role_train' : {        // 武将训练
            1: {
                'timer': 0,     // 冷却到期时间
                'level': 0      // 栏位等级(0, 1, 2)
            },
            2: {
                'timer': 0,
                'level': 0
            },
        },

        'role_refine' : {       // 武将洗点
            'available':0,      // 是否可用
            'defence':0,        // 攻击
            'mdefence':0,       // 魔防
            'attack':0          // 防御
        },

        'role_grouth_give': {   //天赐培养
        },

        'equip' : {             // 装备
        /*
            'id':{              // 装备唯一编号
                'id':           // 装备唯一编号
                'type':         // 类型(物理武器weapon/魔法武器magic/防具armor/饰品accessory)
                'strength':     // 强化等级
                'level':        // 装备等级
                'gems': {       // 宝石
                    slot : gid
                }
                'fail':         // 打造失败次数
                'refine_level':0// 转生等级
                'refine' :{}    // 装备转生后对应的洗练属性值
                'star':         // 星级
            }
        */
        },

        'tavern' : {            // 酒馆
            'roles':[],         // 待招募中的武将id[]
            'recruited':[],     // 已招募位置[](0-9)
            'level':5,          // 招募等级
            'first':1,          // 第一次招募武将的标志,第一次招募之后置0
            'special':{         // 特殊招将, 50-100次后见必出橙将(如果一直没出)
                'count':0,
                'got':0,
            }
        },

        'castle' : {            // 城堡
            'timer':0,          // 上次征收时间
        },

        'farm' : {              // 农场
            'timer':0,          // 上次收获时间
        },

        'task' : {              // 任务
            'main' : {          // 主线任务
                'id' : 1,       // 当前主线任务ID
                'progress' : {
                    '1' : 0,    // 完成条件1
                    '2' : 0,    // 完成条件2
                },
            },

            'branch' : {
                'id' : 0,       // 当前之前任务ID, 0表示没有
                'progress' : {
                    '1' : 0,    // 完成条件1
                    '2' : 0,    // 完成条件2
                },
            }
        },

        'team' : {              // 玩家当前战斗队列
            '0' : 0, 
            '1' : 0,
            '2' : 0,
            '3' : 0,
            '4' : 0,
            '5' : 0,
            '6' : 0,
            '7' : 0,
            '8' : 0,
        },        
        
        'business' : {              // 跑商
            'id':1,                 // 当前前进位置
            'cards':[],             // 选中牌对应的宝石
            'opened':[],            // 翻开的牌的位置
            'back':0,               // 每日返回起点的次数            
        },

        'inventory': {
            'material':{},
            'gem':{},
        /*
            'rtype': {              // 物品类型(material/)
                rid : counter       // 物品Id:物品数量
            }
        */
        },

        'duplicate': 0,                                     // 副本进度

        'tower': {                                          // 爬塔
            'current': {                                    // 不同楼的当前楼层
                /*towerId : floor*/                         // 楼编号:楼层
                1:0,
            },

            'highest': {                                    // 不同楼的历史最高层
                /*towerId : floor*/                         
                1:0,
            },
            'free': 0,                                      // 免费挑战次数
            'cash': 0,                                      // 每日已经付费重置勇闯重楼次数
        },

        'kill': {                                           // 过关斩将
            'progress': 1,                                  // 主进度
            'position': 0,                                  // 关卡位置0-3
            'free': 0,                                      // 免费挑战次数
            'cash': 0,                                      // 每日已经付费重置过关斩将次数 
            'reset':{},               /*battleId : 1*/      // 重置的战役
        },

        'tech': {                                           // 科技
            // id:level                                     // Id对应等级
        },

        'daily_task': {             // 每日任务
            // id:count             // Id对应完成次数
        },

        'activity': {
            //'app_contract_id':'', // 任务集市
            //'contract_task':1,      
            //'contract_task_reward':1,      
            //'contract_task_v3':1,      
            //'contract_task_v3_reward':1,      
            //'block_talk':0,       // 禁聊
            'online_timer':0,       // 上次领取的在线礼包
            'level_pack':0,         // 上次领取的等级礼包
            'system_reward':0,      // 上次领取的系统奖励的ID
            'yellow_first':0,       // 是否领取过黄钻新手
            'blue_first':0,         // 是否领取过蓝钻新手
            'seven_days':0,         // 上次领取7日礼包天数
            'first_pay':0,          // 首充礼包,0没有,1充值未领取,2充值领取
            'paid':0,               // 已经购买元宝数
            'vip_reward':0,         // 上次领取vip奖励等级
            'charge' : {            // 充值特惠活动
                'cash':0,           // 充值的元宝数
                'time':0,           // 上次活动开始时间
                'got' : {
                    /*'id':0,*/     // 充值类型ID,(0/1)已经领取奖励的次数 
                }, 
            },
            'gentry': {             // 土豪活动
                'cash': 0,          // 充值的元宝数
                'time': 0,          // 上次活动开始时间
                'got': 0,           // (0/1)是否领取奖励
            },
            'slot':{                // 幸运宝箱
                /*id:0*/            // 幸运宝箱的ID:已经开启次数
            },              
            'limit_buy':{},         // 今日限量购买已经购买次数
            'offline_xp':{          // 修炼打坐
                'level': gConfGlobal.OfflineXpLevelLimit,        // 开始打坐等级
                'vip': 0,           // 开始打坐vip
                'time': 0,          // 开始打坐时间
            },
            'topone2': {            // 疯狂擂台
                'day': 0,           // 参加活动天(1-7)
                'vote': 0,          // 今日已经投票次数
            },
            'yellow_diamond': {     // 黄钻抽奖活动
                'opened': 0,        // 开通量
                'time': 0,          // 上次活动开始时间
                'got' : {           
                    /*'id':0*/      // 黄钻开通量类型ID,已经领取的奖励次数 
                },         
            },
            'blue_level':0,         // 蓝钻等级礼包
            'wished': 0,            // 是否已经许愿
            'five_role_reward' : 0, // 五虎上将礼包是否已经领取标志(0/1)
            'tower' : 0,            // 勇闯重楼活动已经领取奖励的层数
            'fight_force' : 0,      // 战力大比拼活动已经领取奖励的战斗力
            'forge_level' : {       // 神兵打造活动领奖情况
                /*id:0*/            // id为对应完成项的ID,(0/1)是否已经领取
            },
            'player_level' : 0,     // 冲级大赛活动,已经领取奖励的等级
            'group_on': {           // 百服团购
                'buy' : 0,          // 每日已经购买次数
            },
            'western' : {           // 西域夺宝 
                'time':0,           // 上次活动开启时间
                'id':1,             // 当前前进位置
                'cards':[],         // 选中牌对应的奖励
                'opened':[],        // 翻开的牌的位置
                'back':0,           // 每日返回起点的次数            
            },
            'visit_cottage' : {     // 三顾茅庐
                'count':0,          // 已经连续登陆天数
                'free':0,           // 每日免费拜访次数 
                'got':0,            // 0/1 是否已经获取诸葛亮 
                'cash':0,           // 0/1 是否可以元宝追回诸葛亮
                'visit':0,          // 已经拜访天数
            },
            'wealth' : {            // 财神降临
                'count' : 0,        // 已迎接次数 
                'time' : 0,         // 上次活动开始时间
            },
            'pay_rank' : {          // 充值排行榜
                'paid' : {          // 每日充值情况
                    // day : paid
                },
                'got' : {           // 领奖励情况
                    // day : 1
                },
                'time' : 0,         // 开启时间
                //top10_reward :0   // 是否领取top10奖励
            },
            'hit_box' : {           // 砸箱子
                'hit' : 0,          // 已经砸的次数
                'count' : 0,        // 已经连续登陆天数 
                'got' : 0,          // 是否已经领取奖励
                'free' : 0,         // 每日已免费砸箱子次数
            },
            'cost_gift' : {         // 消费送礼
                'cash' : 0,         // 消费元宝
                'time' : 0,         // 开启时间
                'got' : {},         // 已经领取
                'extra_got' : {},   // 已经领取额外奖励
            },
            'charge_feed' : {       // 充值回馈活动
                'score':0,          // 充值的元宝获得的积分
                'time':0,           // 上次活动开始时间
                'cost' : 0,         // 已经消费的积分
                'exchange': 0,      // 剩余open slot兑换次数
                'got': {},          // 已经兑换
            },
            'new_charge' : {        // 新服充值特惠活动
                'cash':0,           // 充值的元宝数
                'time':0,           // 上次活动开始时间
                'got' : {
                    /*'id':0,*/     // 充值类型ID,(0/1)已经领取奖励的次数 
                }, 
            },
            'my_limit' : {          // 单人限量购买
                'time': 0,
                'buy': {},
            },

            'daily_pay_rank' : {    // 每日充值排行榜
                'paid': {},         // 日充值数
                'time': 0,          // 上次活动时间
            },

            'pet_gift' : {          // 神兽进阶
                'time': 0,          // 上次活动时间
                'got': {},          // 已经领取
            },

            'break_egg' : {         // 砸蛋
                'free': 0,          // 每日免费砸蛋次数
                'timer': 0,         // 领奖冷却时间
                'reward': {},       // 奖励
                'got': 0,           // 奖励领取(>0 表示有奖励, <0表示领取完毕)
            },

            'charge_role' : {       // 充值神将
                'time': 0,          // 上次活动时间
                'cash': 0,          // 已经充值数
                'got': {},          // 已经领取
            },

            'charge_limit' : {      // 充值限量购买
                'time': 0,          // 上次活动时间
                'cash': 0,          // 已经充值数
                'got': {},          // 已经领取
                'buy': {},          // 已经购买
            },

            'chargegod_limit' : {   // 充值全民觉醒
                'time': 0,          // 上次活动时间
                'cash': 0,          // 已经充值数
                'got': {},          // 已经领取
                'buy': {},          // 已经购买
            },

            'cdkey' : {             // CDKEY领取
            },

            'star_gift' : {         // 冲星福利领取
                'time':0,           // 上次活动时间
                'got':{},           // 领取情况
            },

            'diamond_card' : {      // 钻石卡牌
                'free': 0,          // 每日免费次数
                'cash': 0,          // 每日付费次数
                'cards': [],        // 刷新的牌
                'open' : {},        // 翻出的牌 pos: cardIndex
            },

            'server_payrank' : {    // 跨服充值排行榜
                'time': 0,          // 上次活动时间
                'cash': 0,          // 充值元宝
                'reward': {},       // 全民奖励领取 rank : 0/1
            },

            'mystic_shop' : {       // 神秘商店
            },

            'lucky_number': {       // 幸运号码
                'got' :  0,         // 0/1 是否领奖
                'selected' : 0,     // 0/1 是否选取号码
                'day' : 0,          // 参加活动的天 eg:20140101
            },
            
            'red_packet' : {        // 新年红包
                'got' : {
                    /*'cash:0'*/    // 对应花费元宝奖励是否领取
                },
                'open' : 0,         // 红包每日已经开启次数
                'day' :0,           // 活动的日期 eg:20140101
            },

            'year_boss': {              // 年兽
                'join': 0,              // 参加挑战的day
                'got_reward':0,         // 领取奖励的day
                'cold_time':0,          // boss开始冷却的时间
                'inspire':0,            // 鼓舞次数
                'bomb_time':0,          // 上次领取爆竹的时间
            },

            'rich_send' : {         // 土豪送礼
                'time': 0,          // 上次活动时间
                'cash': 0,          // 充值元宝
                'day': 0,           // 奖励日期
                'reward': {},       // 全民奖励
            },

            'daily_shop_cost': {    // 商店每日消费情况
                'cash': 0,          // 每日商店花费元宝数
                'day' : 0,          // 活动的日期 eg:20140101
            },

            'new_red_packet' : {    // 新区红包
                'got' : {
                    /*'cash:0'*/    // 对应花费元宝奖励是否领取
                },
                'cash' : 0,         // 消费数量
                'open' : 0,         // 红包每日已经开启次数
                'day' :0,           // 活动的日期 eg:20140101
            },

            'super_card' : {        // 超级卡牌
                'free': 0,          // 每日免费次数
                'cash': 0,          // 每日付费次数
                'cost1': 0,         // 消耗元宝1
                'cost2': 0,         // 消耗元宝2
                'cards': [],        // 刷新的牌
                'open' : {},        // 翻出的牌 pos: cardIndex
            },

            'login_invest' : {      // 登陆投资
                'cash' : 0,         // 投资元宝数 
                'count' : 0,        // 已经登陆天数(投资当天开始记数)
                'got' : {            
                    /*day:0*/        // 0/1 是否领取对应奖励
                },
            },

            'level_invest' : {      // 升级投资
                'cash' : 0,         // 投资元宝数
                'got' : {
                    /*level:0*/     // 0/1 是否领取对应等级的奖励
                },
            },

            'new_first_pay' : {     // 新首充
                /*cash : 0*/        // 充值到达的元宝数cash : 0/1 是否领取奖励
            },

            'new_forge_level' : {   // 新神兵打造 
                'equip' :{          // 活动期间打造的各种装备的最高等级
                    'weapon' : 0,  
                    'armor' : 0,
                    'accessory' : 0,
                    'magic' : 0,
                },
                'got' : {
                    /*id:0*/        //  0/1 对应奖励是否领取  
                },
            },
            
            /*
            'league_king' : {       // 王者军团的奖励是否被领取
                id:0,       
                leader_group:id,
            },
            */

            'qq_panel': 0,          // qq面板奖励领取

            'facebook_feed':0,      // facebook每日分享奖励

            'dig_treasure' : {      // 地下宝藏
                'free' : 0,         // 每日免费已经重置次数 
                'cash' : 0,         // 每日元宝已经重置次数
                'depth' : 1,        // 当前挖宝的层数 
                'axe' : 0,          // 本次活动剩余的铁镐次数
                'open' : {          // 宝藏打开的情况
                    /*pos:{         // 位置[1-36]
                        id : 0,     // 对应的物品ID
                        got : 0,    // 0/1 是否完成挖掘 
                    }
                    */      
                },
                'time' : 0,         // 上次活动开启的时间
                'day' : 0,          // 上次获取免费铁镐的日期eg:20140101
            },

            'pet_cost' : {          // 全民神兽进阶
                'time' : 0,         // 上次活动时间
                'got' : 0,          // 已领取阶
                'used' : {          // 已经使用情况
                    /*level:num*/   // 等阶:消耗神兽丹数
                },
            },

            'vip_box' : {           // 特权宝箱
                'time' : 0,         // 上次活动时间
                'box_count' : {
                    /*id:count*/    // 箱子ID:已购买数
                },
            },
            
            /*
            'login_count' : {       // 活动期间登录天数
                'time' : 0,         // 上次活动时间
                'count' : 0,        // 已经登录天数
            },
            */

            'all_gift' : {          // 全民福利
                'time' : 0,         // 上次活动开启的时间 
                'day' : 0,          // 充值当日日期
                'paid' : 0,         // 个人今日充值元宝
                'got' : {           // 今日奖励领取情况
                    'all' : 0,      // 0/1 全民福利
                    'self' : 0,     // 0/1 个人充值高级福利
                },
            },

            'lucky_circle' : {      // 幸运轮盘 
                'score' : 0,        // 玩家拥有的兑换积分
                'items' : [],       // 刷新的物品
                'count' : 0,        // 抽奖已经抽取的次数,每次刷新时重置
                'color' : {         // 各个颜色格子拥有的个数(每日重置)
                    'green' : 0,
                    'orange': 0,
                    'purple': 0,
                    'red'   : 0,
                },
                'got' : {           // 每日奖励已经领取状态
                    'green' : 0,    // 0/1 是否已经领取
                    'orange': 0,
                    'purple': 0,
                    'red'   : 0,
                },
                'cash': 0,          // 每日已经元宝刷新的次数
                'update_time': 0,   // 上次免费刷新的时间 
                'time': 0,          // 上次活动时间
                'day' : 0,          // 活动的天 20140101
            },

            'all_charge' : {        // 全民充值
                'cash' : 0,         // 个人已经充值金额
                'time' : 0,         // 上次活动的时间
                'got' : {},         // 已经领取奖励情况
            },

            'year_feed_back' : {    // 周年回馈 
                'cash' : 0,         // 个人消费元宝
                'time' : 0,         // 开启时间
                'got' : {},         // 已经领取奖励情况
            },

            'national_seven' : {    // 国庆七天乐
                'day' : 0,          // 登录天数
                'time' : 0,         // 开启时间
                'got' : {},         // 已经领取奖励情况
            },
        },

        'sign' : {                  // 每日签到
            'first' : 0,            // 首次签到的日期"2013-01-01" 
            'records':0,            // 记录签到情况(0-29位,1表示已经签到，0表示未签到)
            'count':0,              // 已经签到的次数
            'got':{                 // 奖品的获取情况(0未获取,1已经获取)
                '5' : 0,
                '10':0,
                '15':0,
                '20':0,
                '25':0,
            },
            'current':0,            // 当前应该签到的位置[0-29]
        },

        'login' : {                 // 连续登陆
            'first' : common.getDateString(),// 首次登陆"2013-01-01" 
            'count' : 0,            // 记录连续登陆的天数
            'got':{                 // 奖品的获取的情况(0未获取,1已经获取)
                '1':0,
                '2':0,
                '3':0,
                '4':0,
                '5':0,
            },
        },

        'league' : {                // 军团
            'applying':{            // 正在申请的军团,最多只有3个
                /*'name' : time,*/  // 申请的军团名称:申请的时间
             },
            'name':0,               // 加入的军团名字, 0表示未加入任何军团
            'contribute':0,         // 个人军团贡献
            'opened':{              // 翻牌
                /*pos:number*/      
            },
            'tech' : {              // 军团科技
                /*id : level*/ 
            },
        },

        'prison' : {                // 地牢
            'prisons': {
                1 : 0,              // {uid,name,headpic,timer,output}
                2 : 0,              // 抓捕到的玩家id,名字,头像,抓捕时间,金币产出
                3 : 0,
                4 : 0,
                5 : 0
            },
                    
            'free': 0,              // 每日免费抓捕次数
            'cash': 0,              // 每日付费抓捕次数
            'enemy': 0,             // 被抓捕
        },

        'mine' : {                  // 金矿
            'occupied': 0,          // 今日免费已占领次数
            'cash': 0,              // 今日已付费占领次数
            'occupying': 0,         // 当前占领 0未占领 {level:, id:, pos:}
            'occupied_time': 0,     // 占领时间
        },

        'rank' : {
            'free': 0,              // 每日免费挑战次数
            'cash': 0,              // 每日付费挑战次数
            'timer': 0,             // 上次领取奖励时间
        },    

        'combine' : {               // 合璧
            'stone' : 0,            // 半块玉璧ID 
            'got' : 0,              // 每日领取次数
            'combined' : 0,         // 每日合璧次数
            'occupying' : 0,        // 当前占领 0未占领 {level:,id:}
        },

        'wheel':{                   // 命运转轮活动
            'free':0,               // 每日转轮的免费次数
            'star':0,               // 已经获取的star数目
            'star_cash':0,          // 当前星星的付费次数
            'first_start':1,        // 该玩家是不是第一次玩
            'star_cash_total':0,    // 各个星星付费总次数
        },

        'league_war' : {            // 军团战
            'energy' : 0,           // 上次领取体力后盈余的体力
            'energy_time' : 0,      // 上次领取体力的时间
            'ex_energy_time':0,     // 上次获取额外体力的时间
            'buy_energy':0,         // 每日已经付费购买体力的次数
            'move_time': 0,         // 上次结算迁徙的时间 
            'move':1,               // 迁徙次数
            'glory':0,              // 军团荣誉(每次军团战结束时获取)
            'grade_level':1,        // 军衔等级
            'grade_xp':0,           // 军衔经验
            'last_fight_time':0,    // 上一次进攻时的时间
        },

        'invest' : {                // 投资活动
            'got': 0,               // 上次领过的奖励1,3,7
            'invested': 0,          // 投资的项目
            'time':'',              // 投资的时间
        },

        'boss' : {
            'join':0 ,              // 参加挑战的day
            'got_reward':0,         // 领取奖励的day
            'time': 0,              // 开始冷却时间
            'inspire':0,            // 鼓舞次数
        },

        'black_shop':{              // 黑市商店
            'timer' : 0,            // 上次商店物品刷新的时间
            'sell':{                
                /* id : 0*/         // 正在出售的物品ID,(0/1)是否已经出售 
            },
            'limit_sell':{          // 每天限售次数的物品,每日清空
                /* id : 0*/         // 正在出售的物品ID,已经出售的次数 
            }
        },

        'surprise': {               // 步步惊喜
            'grid': 1,              // 格子编号
            'free': 0,              // 使用的次数(先免费,后付费)
        },

        'tomb': {                   // 古墓宝藏 
            'kill': [0, 0, 0],      // 3个boss击杀情况
            'dig': [0, 0, 0],       // 3个boss的挖宝次数
            'got': [0, 0, 0],       // 3个boss的击杀奖领取情况 
        },

        'server_war': {             // 跨服战
            'save_team':0,          // 0/1 是否已保存报名赛期间的阵型
            'support':{
                /*progress:{        // progress:(16,8,4,2,1)
                    uid:0,          // 32强玩家UID
                    got:0,          // 是否已经领取支持奖励
                    level:0,        // 支持者自己支持时的君主等级
                    succ:0,         // 支持的玩家是否胜利
                }*/  
            },
            'top_reward':0,         // 0/1 是否领取32强奖励
            'count' : 0,            // 玩家支持跨服战时,当前跨服战进行的次数
        },

        'achievement': {            // 成就系统
            'stage': 1,             // 成就阶段
            'goal': {},             // 成就达成状态 0未完成/1完成/2领取完奖励
            'reserved':{}           // 保留数据,用于服务器计算
        },

        'forge': {                  // 神兵锻造
            'total': 0,             // 总共锻造次数
            'today': 0,             // 今日锻造次数
            'got'  : 0,             // 是否已经领取
        },
        
        'world_boss': {             // 世界boss
            'join': 0,              // 参加挑战的day
            'today_id':0,           // 参加挑战的day的boss ID(1/2, 第一个boss/第二个boss)
            'got_reward':0,         // 领取奖励的day
            'time':0,               // 开始冷却的时间
            'inspire':0,            // 鼓舞次数

        },

        'day_wheel' : {             // 天天爱转轮 
            'reward_id' : 1,        // 奖励编号ID 
            'update_count':0,       // 自动刷新连续登陆天数
            'lottery_count':0,      // 抽奖连续登陆天数
            'free':0,               // 每日已免费抽奖次数
            'cash':0,               // 每日已付费抽奖次数
        },

        'pet': {                    // 神兽
            'level': 1,             // 等阶
            'bless': 0,             // 祝福
            'bless_timer': 0,       // 祝福失败计时器
            'bless_count': 0,       // 祝福次数
            'skills': [],           // 技能排序
            'skill_levels':{'1':1}, // 技能等级
            'buff': 0,              // 幻化丹数量
        },

        'special_role': {           // 本命武将
            'name' : '',            // 自定义名字
        },

        'cloud_shop' : {            // 云购
            'score':0,              // 积分
            'exchange':0,           // 兑换次数(判断特殊奖励)
            'buys':{
            //    times: seqs[]     // 期 => 序号
            }
        },

        'battle_world' : {          // 决战天下
            'buy_energy':0,         // 每日已经付费购买体力的次数
            'energy' : 0,           // 上次领取体力后盈余的体力
            'energy_time' : 0,      // 上次领取体力的时间
            'move_time' : 0,        // 上次结算迁徙时间 
            'move':1,               // 迁徙次数 
            'support':{
                /*progress:{        // progress:(9,3,1)
                    server:0,       // 所支持的服务器ID 
                    got:0,          // 是否已经领取支持奖励
                    level:0,        // 支持者自己支持时的君主等级
                    succ:0,         // 支持的玩家是否胜利
                }*/  
            },
            'count' : 0,            // 玩家支持决战天下时,当前决战天下进行的次数
            'final_reward' : 0,     // 0/1 是否领取本届决战天下的最终奖励
            'reward' : 0,           // progress(9,3,1)玩家已经领取的阶段比赛奖励
            'last_fight_time':0,    // 上一次进攻的时间
        },

        'daily_pay' : {             // 每日充值
            'day' : 0,              // 每日充值天
            'cash': 0,              // 今日充值
            'reward': 0,            // 今日奖励
            'month' : '',           // 充值月开始日期
            'month_pay': 0,         // 月每日充值次数
            'month_day': 0,         // 月充值日
        },

        'daily_vars' : {            // 每日变量
            'day' : 0,              // 变量组时间(天)

            'friend_point':0,       // 每日获取的义气点数
            /*
            cost_cash                   // 今日消费
            pay_cash                    // 今日充值
            pay_carnival_award          // 今日充值嘉年华领奖标志
            */

            'qqpcmgr_signed': 0,    // 今日QQ管家是否已签到(今日已经登录过QQ电脑管家)
        },

        'pay_carnival' : {          // 充值嘉年华
            'total_cash' : 0,       // 此轮总充值
            'reach_time' : 0,       // 此轮达成时间
            'award_step' : 0,       // 领奖步骤
        },

        'huatuo' : {                // 华佗炼丹
            'xp' : 0,               // 炼制经验
            'score' : 0,            // 积分
            'free' : 0,             // 今日免费炼制次数
            'cash' : 0,             // 今日元宝炼制次数
            'pellet': 0,            // 炼制结果
            'quantity' : 0,         // 提炼品阶
        },

        'troop_match' : {
            'max_win' : 0,          // 最大连胜
            'cur_win' : 0,          // 当前胜场
            'win_streak' : 0,       // 连胜场数
            'lose' : 0,             // 失败次数
            'score' : 0,            // 积分
            'time' : 0,             // 上次进入时间
            'clear_time':0,         // 今日已经清除time次数,改为每天进入次数
            'roles' : [],           // 获取的武将
            'options' : [],         // 可选武将
            'team' : {},            // 阵形
            'week' : 0,             // 周
            'week_score' : 0,       // 周积分
        },

        'server_mine' : {           // 跨服金矿
            'free' : 0,
            'occupied_time': 0,     // 占领时间
        },
        
        'invite' : {                // 邀请数据
            'goal' : {},            // 目标领奖情况
            'stage' : {},           // 阶段领奖情况
        },

        'friend' : {                // 好友信息
            'applying' : 0,         // 正在申请好友的数量
            'friend' : {},          // 已经添加为好友的UID
            'shield' : {},          // 被屏蔽的UID
            'black' : {},           // 被拉黑的UID
        },

        'ride' : {                  // 坐骑 
            'horse_currency' : 0,   // 玩家拥有的马币数量
            'gold' : 0,             // 每日已经金币套马次数
            'horse' : {
                /*
                id : {              // 系统生成唯一ID  
                    horse_id : 0,   // 马匹ID
                    level : 0,      // 马匹等级 
                }
                */ 
            },
        },

        'mybattle' : {              // 个人战役
            'free' : 0,             // 每日已经免费重置战役次数
            'cash' : 0,             // 每日已经付费重置战役次数
            'max' : 0,              // 已经通关的最大关卡ID
            'progress' : {          // 各个战役进度
                /*
                battleId : id,      // 战役编号:关卡ID
                */
            },
        },

        'lottery' : {               // 大乐透
           'score' : 0,             // 积分 
           'day' : 0,               // 购买日期 eg: 20140101
           'got': 0,                // 是否领奖
           'count' : 0,             // 今日购买注数
        },

        'bless_dice' : {            // 福星高照
            'score' : 0,            // 福缘积分 
            'count' : 0,            // 今日投掷骰子次数
            'free'  : 0,            // 今日免费改运次数
            'cash'  : 0,            // 元宝改运次数
            'cur_round' : 0,        // 当前n(配置)个骰子结果 eg: 111111表示6个骰子都是福字 ,为0表示未投掷
        },

        'league_duplicate' : {      // 军团副本
            'inspire' : 0,             // 鼓舞等级
            'attack_times' : 0,         // 已经挑战次数
            'buy_times' : 0,            // 已经购买次数
        },

        'machao_box' : {            // 义气点马超宝箱
            'id' : -1,              // 宝箱配置ID
            'progress' : 0,         // 宝箱进度
        },

        'xingzuo_romance' : {       // 星座奇缘
            'point' : 0,            // 积分
            'candy' : 0,            // 糖果积分
            'pos' : 1,              // 当前位置
            'normal' : 0,           // 今日普通占星次数
            'high' : 0,             // 今日高级占星次数
        },

        'qqpcmgr' : {               // QQ电脑管家
            'new_user_award_time' : 0,      // 新手奖励领取时间戳
            'sign_count': 0,        // 签到计数（每7天一个轮回）
        },
    };

    return initUser;
}

Player.prototype = {
    init : function(fields, callback) {
        if( fields && (typeof(fields) == 'object') && Object.keys(fields).length > 0 ){
            // 加载玩家部分数据
            fields['ai'] = 1;
        }

        var player = this;

        // 读取玩家数据
        gDBUser.findOne({_id : player.uid}, fields, function(err, doc){
            if( !doc) {
                if( err ) {
                    callback && callback(false);
                }else{
                    //创建新用户
                    player.user = Player.create(player.uid);
                    player.addRole(gDefaultRoleId);
                    player.user.team[3] = gDefaultRoleId;
                                        
                    for( var i=1; i<=4; i++ ) {
                        player.addEquip('weapon', 1); 
                        player.addEquip('magic', 1); 
                        player.addEquip('armor', 1);
                        player.addEquip('accessory', 1);
                    }
                    
                    player.cleanDirty();

                    gDBUser.insert(player.user, function(err, result){
                        if( err ) {
                            callback && callback(false);
                        }else{
                            player.status.newRole = true;
                            callback && callback(true);
                        }
                    });
                }
            }else {
                try {
                    player.quarterActionScore = doc.quarterActionScore||0;
                    player.lastMsgQuarter = doc.lastMsgQuarter||0;
                    delete doc.quarterActionScore;
                    delete doc.lastMsgQuarter;
                    player.user = doc;

                    var league = player.user.league;
                    if (league) {
                        for (var techId in league.tech) {
                            var techLevel = +league.tech[techId];
                            if (techLevel > gConfRuntime.LeagueTechMaxLevel) {
                                league.tech[techId] = gConfRuntime.LeagueTechMaxLevel;
                                player.markDirty('league.tech.' + techId);
                            }
                        }
                    }

                    player.checkInventory(); //检查包裹,删除过期物品
                    player.checkRoleEquip(); //查武将,装备数据
                    player.addXp(0); //新等级
                    player.getVip(); //更新vip
                    player.getFightForce(); //新战斗力
                } catch (e) {
                    ERROR(e.stack);    
                    callback && callback(false);
                }

                callback && callback(true);
            }
        });
    },

    initWithUniverseData : function(user) {
        this.user = user;
        this.roleAttrs = user._role_attr;
    },

    mergeDirty: function() {
        // 合并写入
        var haveDirty = false;
        for (var key in this.dirty) {
            haveDirty = true;
            this.allDirty[key] = this.dirty[key];
        }

        this.dirty = {};
        if (haveDirty) {
            this.saveCount += 1;
        }
    },

    save : function(force, callback) {
        this.mergeDirty();
        
        if( (!force && this.saveCount < 10) || (Object.keys(this.allDirty).length == 0) ) {
            // 10次数据库操作必写入
            callback && callback(true);
            return;
        }

        var updates = {$set: {'active_time': this.lastActive-this.user.info.create, 
            'quarterActionScore': this.quarterActionScore,
            'lastMsgQuarter': this.lastMsgQuarter}, 
            $unset: {}};
        var arrangedDirty = this.arrangeDirty(this.allDirty);
        for( var item in arrangedDirty ) {
            var remove = arrangedDirty[item];
            if( remove ) {
                updates['$unset'][item] = 1;
            }else{
                var obj = this.user;
                var args = item.split(".");
                var ok = true;
                for(var i=0; i<args.length; i++ ) {
                    if( typeof(obj) != 'object' ) {
                        // 未找到
                        ok = false;
                        break;
                    }
                    obj = obj[args[i]];
                }

                if( ok && obj != undefined && obj != NaN && obj != null ) {
                    updates['$set'][item] = obj;
                }else{
                    ERROR('invalid save: '+item);
                }
            }
        }

        this.allDirty = {};
        this.saveCount = 0;
        this.isSaving = true;

        gDBUser.update({_id : this.uid}, updates, function(err, result) {
            this.isSaving = false;
            if( err ) {
                ERROR(util.format('%d SAVE %j %j', this.uid, updates, err));
                this.saveError = true;
                callback && callback(false);
            }else {
                LOG(util.format('%d SAVE %j', this.uid, updates));
                callback && callback(true);
            }
        }.bind(this));
    },

    saveAll : function(){
        gDBUser.save(this.user, function(err, result){
        });
    },

    nextId : function() {
        this.user.ai += 1;
        this.markDirty('ai');
        return this.user.ai;
    },

    arrangeDirty : function(dirty) {
        var arrangedDirty = {};

        for( var item in dirty ) {
            var dirtyType = dirty[item];
            
            var needRemove = [];
            var addNew = true;
            var levels = item.split('.');
            for( var eitem in arrangedDirty ) {
                var elevels = eitem.split('.');
                if( elevels.length == levels.length ) continue;
                var minLen = Math.min(elevels.length, levels.length);
                    
                var isTree = true;
                for( var i=0; i<minLen; i++ ) {
                    if( elevels[i] != levels[i] ) {
                        isTree = false;
                        break;
                    }
                }

                if( !isTree ) continue;

                if( elevels.length < levels.length ) {
                    // 更低级别的变更,抛弃
                    addNew = false;
                    break;
                }else{
                    // 更高级别的变更
                    needRemove.push(eitem);
                }
            }
            
            needRemove.forEach(function(removeItem){
                delete arrangedDirty[removeItem];
            });

            if( addNew ) {
                arrangedDirty[item] = dirtyType;
            }
        }
        return arrangedDirty;
    },
    
    // 标志需要写入的变更数据名 a.b格式
    markDirty : function(item) {
        this.dirty[item] = 0;
    },

    markDelete : function(item) {
        this.dirty[item] = 1;
    },

    cleanDirty : function() {
        this.dirty = {};
    },

    
    // ---------------玩家逻辑相关------------------

    getOnlineTime : function() {
        if (!this.loginStamp) {
            return 0;
        }
        return Date.getStamp() - this.loginStamp;
    },

    addXp : function(xp) {
        if( xp < 0 ) return;
        
        var user = this.user;
        var status = user.status;
        var oldLevel = status.level;
        status.xp += xp;
        var newLevel = this.getLevel();
        if( !gConfLevel[newLevel+1] ) {
            // 达到最高等级
            var maxXp = gConfLevel[newLevel].Xp - 1;
            if( status.xp > maxXp ) {
                status.xp = maxXp;
            }
        }

        status.level = newLevel;

        if( xp != 0 ) {
            this.markDirty('status.xp');
        }
        
        if( oldLevel != newLevel ) {
            this.markDirty('status.level');
            
            var worldReq = {
                uid: this.uid,
                mod: 'rank',
                act: 'update_level',
                args: {
                    name: this.user.info.un,
                    headpic: this.user.info.headpic,
                    level: newLevel,
                }
            };

            var worldResp = {};
            requestWorld(worldReq, worldResp);

            var totalOnlineTime = user.mark.online_time || 0 + this.getOnlineTime();
            //RoleLevelUp|iEventId|iWorldId|iUin|dtEventTime|vClientIp|iRoleId|vRoleName|iJobId|iGender|iRoleLevel|iRoleExp|iTotalOnlineTime|iReason
            writeRecord(util.format('RoleLevelUp|%s|%d|%s|%s|%s|%d|RoleName|0|0|%d|%d|%d|addXp',
                this.action.getEvent(), config.ServerId, this.openid, Date.stdFormatedString(), this.clientIP, 
                this.uid, status.level, status.xp, totalOnlineTime));

            if (config.ServerId && config.ServerId >= 40) {
                var loginCountActivitySchedule = findActivitySchedule('logincount');
                if (loginCountActivitySchedule && this.user.activity.kaishui_promotion && (6 == newLevel)) {
                    player = this;
                    doKaiShuiPromotion(player);
                }
            }
        }

        if( newLevel == 13 ) {
            this.updateWorld();
        }
    },

    getLevel : function() {
        var xp = this.user.status.xp;
        var i = 1;
        for( i=1; i<1000; i++ ) {
            if( !gConfLevel[i+1] || gConfLevel[i].Xp > xp ) {
                break;
            }
        }

        return i;
    },

    // 消耗资源接口,num为正数
    costResource : function(name, num, arg1, arg2) {
        return this.alterResource(name, -num, arg1, arg2);
    },
    
    // 修改资源接口,num为正数表示添加,负数表示消耗
    alterResource: function(name, num, arg1, arg2) {
        if (isNaN(num)) {
            return false; 
        }

        if (!num) {
            return true;
        }

        var status = this.user.status;
        var newNum = (status[name] || 0) + num;
        if (newNum < 0) {
            return false;
        }

        switch (name) {
            case 'cash':
                status.cash = newNum;
               this._saveCashLog(num, arg1, arg2); 
               break;
            case 'honor':
            case 'gold':
            case 'food':
            case 'soul':
            case 'hsoul':
                status[name] = newNum;
                break;
            default:
                return false;
        }

        this.markDirty('status.'+name);
        return true;
    },

    _saveCashLog: function(num, arg1, arg2) {
        var user = this.user;
        var uid = this.uid;
        var now = new Date();
        var action = this.action.getEvent();

        if (num < 0) {
            var cash = -num;
            this.saveCashCost(cash);

            reportQQ('consume', {'opuid':uid, 'modifyfee':cash}, user.info.platform);

            //Consume|iEventId|iWorldId|iBuyerUin|iBuyerRoleId|vBuyerName|iBuyerJob|iBuyerLevel|dtEventTime|vClientIp|
            //iTradeType|iUin|iRoleId|vRoleName|iJob|iRoleLevel|iCostType|iCost|iGameGold|iPubacct|iMoney|iRemain|
            //iShopType|iGoodsType|iGoodsId|iGoodsNum|iRebateType|iRebateRate|iItemGuid|vReMarks|iLoginWay
            costType = ("inventory.buy" == action) ? 1 : 2;
            writeRecord(util.format("Consume|%s|%d|0|0|0|0|0|%s|%s|0|%s|%d|RoleName|0|%d|1|%d|0|0|0|%d|%s|%d|%d|%d|0|0|0|sn|0",
                action, config.ServerId, now.stdFormatedString(), this.clientIP, 
                this.openid, uid, user.status.level, cash, user.status.cash,
                0, costType, arg1||0, arg2||1));
        }
        
        var cashLog = {
            uid:uid, 
            mod: this.action.mod,
            act: this.action.act,
            num: num, 
            remain: user.status.cash,
            level: user.status.level,
            arg1: arg1, 
            arg2: arg2, 
            time: now.getStamp(),
        }
        gDBCash.insert(cashLog, function(err, result){});
    },

    getFightForce : function() {
        var fightForce = this.calculateTeamRoleAttr();
        var oldFightForce = this.user.status.fight_force;
        if (fightForce != oldFightForce) {
            this.user.status.fight_force = fightForce;
            this.markDirty('status.fight_force');
            if (fightForce > oldFightForce) {
                this.checkEvent('FightForce');
            }
        }

        return fightForce;
    },

    pay : function(cash) {
        this.user.activity.paid += cash;
        var paid = this.user.activity.paid;
        for( i=1; i<1000; i++ ) {
            if( !gConfVip[i] || gConfVip[i].Cash > paid ) {
                break;
            }
        }

        this.user.status.vip = i-1;

        this.markDirty('activity.paid');
        this.markDirty('status.vip');
    },
    
    updateWorld : function(){
        // 更新世界服务器中玩家基本信息
        var user = this.user;
        var roles = {};
        for( var pos in user.team ) {
            var rid = user.team[pos];
            if( rid ) {
                roles[rid] = user.role[rid].fight_force;
            }
        }

        var worldReq = {
            uid: this.uid,
            mod: 'user',
            act: 'update',
            args: {
                'name' : user.info.un,
                'headpic' : user.info.headpic,
                'blue' : user.info.blue,
                'yellow': user.info.yellow,
                'blue_year': user.info.blue_year,
                'yellow_year': user.info.yellow_year,
                'level' : user.status.level,
                'fight_force' : user.status.fight_force,
                'roles' : roles,
                'league': user.league.name ? user.league.name : '',
                'invited': user.info.invited || '',
            }
        };

        var worldResp = { };
        requestWorld(worldReq, worldResp);
    },

    // ---------------武将逻辑相关------------------

    checkRole: function() {
        if (gConfSwitch.TransDefaultRole) {
            var rid = gDefaultRoleId;
            var role = this.user.role[rid];
            if (!role) {
                this.addRole(rid);
            } else {
                var soldierLevel = role.soldier_level || 1;
                var minGrouth = (soldierLevel > 1) ? gConfGrouth['orange']['Min'+soldierLevel] : gConfRole[rid].Grouth;
                if (role.grouth < minGrouth) {
                    role.grouth = minGrouth;
                    this.markDirty(util.format('role.%d.grouth', rid));
                }
            }
        }
    },

    canEnrollRole : function(rid) {
        var existRoleNum = Object.keys(this.user.role).length;
        if( (rid in this.user.role) || existRoleNum >= gConfGlobal.RoleSeatLimit ) {
            return false;
        }
        return true;
    },
    
    // 传入role对象或者rid, 如果是role会被删除掉
    roleToCard : function(role) {
        var rid = role;
        if( role.id ) {
            rid = role.id; 
            delete this.user.role[rid];
            this.markDelete('role.'+rid);
        }
        this.addRoleCard(rid, 1);
    },

    addRoleCard : function(rid, num){
        var existCardNum = this.user.role_card[rid] || 0;
        var newCardNum = existCardNum + num;
        if( newCardNum < 0 ) {
            return false;
        }

        this.user.role_card[rid] = newCardNum;
        this.markDirty('role_card.'+rid);
        this._recordItem('card', rid, num, existCardNum);
        return true;
    },

    addRole : function(rid) {
        if( !this.canEnrollRole(rid) ) {
            // 转换为武将卡
            this.roleToCard(rid);
            return false;
        }

        var skillLevels = {};
        skillLevels[1] = 1;
        var roleConf = gConfRole[rid];
        var soldierKind = roleConf.Soldier;
        var role = {
            'id':rid,                   // 编号
            'xp':0,                     // 经验
            'level':1,                  // 等级
            'soldier_level':1,          // 兵种等级
            'grouth':roleConf.Grouth,   // 成长值
            'attack':0,                 // 洗点的攻击
            'defence':0,                // 洗点的防御
            'mdefence':0,               // 洗点的魔防
            'weapon':0,                 // 武器
            'armor':0,                  // 防具
            'accessory':0,              // 饰品
            'skill':1,                  // 选择的技能
            'skill_levels':skillLevels, // 技能等级
            'soul':0,
            'fight_force':0,            // 战斗力
            'god':0,                    // 神将等级
            'soul_break':0,             // 炼魂突破
            'grouth_break':0,           // 培养突破
        };
        
        this.user.role[rid] = role;
        this.markDirty('role.'+rid);

        this.calculateRoleAttr(role);
        this.doEventAddRole(role);

        LOG(util.format('%s %s %s %j', this.uid, 'SystemLog', 'addRole', {id: rid}));

        return role;
    },

    addRoleXp : function(role, xp) {
        //var oldLevel = this.getRoleLevel(role);
        //if( xp > 0 && oldLevel >= this.user.status.level ) {
        //    return false; 
        //}

        role.xp += +xp;
        var newLevel = this.getRoleLevel(role);
        var maxLevel = Math.min(this.user.status.level, gConfRoleLevelMax);
        if(newLevel >= maxLevel) {
            newLevel = maxLevel;
            var maxXp = gConfRoleLevel[maxLevel].Xp - 1;
            if( role.xp > maxXp ) {
                role.xp = maxXp;
            }
        }
        
        var levelChange = false;
        if(role.level != newLevel) {
            levelChange = true;
            role.level = newLevel;
            this.markDirty(['role',role.id,'level'].join('.'));
            this.checkEvent('HaveLevelRole', role.level);
        }

        if( xp != 0 ) {
            this.markDirty(['role',role.id,'xp'].join('.'));
        }

        return levelChange;
    },

    getRoleLevel : function(role) {
        var xp = role.xp;
        var i = 1;
        for( i=1; i<1000; i++ ) {
            if( !gConfRoleLevel[i+1] || gConfRoleLevel[i].Xp > xp ) {
                break;
            }
        }

        return i;
    },

    calculateAllRoleAttr: function() {
        var roles = this.user.role;
        var me = this;
        objectForEach(roles, function(rid, role) {
            me.calculateRoleAttr(role); 
        });
    },

    calculateTeamRoleAttr : function(force) {
        if (force || this.roleDirty) {
            this.battleWorldSaveTeam = true;
        } else {
            return this.user.status.fight_force;
        }

        this.roleDirty = false;
        var roles = this.user.role;
        var team = this.user.team;
        var needCalculate = true;

        if (this.user.role_images) {
            needCalculate = false;
            this.calculateAllRoleAttr();
            this.updateRoleImages(force);
        }

        var fightForce = 0;
        for (var pos in team) {
            var rid = team[pos];
            if (!rid) {
                continue;
            }

            var role = roles[rid];
            if (!role) {
                team[pos] = 0;
                continue;
            }

            if (needCalculate) {
                this.calculateRoleAttr(role); 
            }

            fightForce += role.fight_force;
        }

        return fightForce;
    },

    calculateRoleAttr : function(role) {
        var roleConf = gConfRole[role.id];
        var soldierKind = roleConf.Soldier;

        // 洗练的属性
        var attrs = {
            'attack': role.attack, 
            'defence': role.defence,
            'mdefence': role.mdefence, 
            'health': 0,
            'fortitude': roleConf.Fortitude,
            'hit': roleConf.Hit,
            'unblock': roleConf.Unblock,
            'block' : roleConf.Block,
            'critdamage': roleConf.Critdamage,
            'miss': roleConf.Miss,
        };

        // 根据等级/成长值计算基础
        if( 5 == roleConf.ReceiveMode ) {
            var specialRoleGrouthAdds = 1 + role.level * 0.002;
            attrs.attack += roleConf.Attack + Math.floor(role.grouth * 
                            roleConf.AttackGrouth / 100 * role.level * specialRoleGrouthAdds);
            attrs.defence += roleConf.Defence + Math.floor(role.grouth * 
                            roleConf.DefenceGrouth / 100 * role.level * specialRoleGrouthAdds);
            attrs.mdefence += roleConf.MagicDefence + Math.floor(role.grouth * 
                            roleConf.MagicDefenceGrouth / 100 * role.level * specialRoleGrouthAdds);
        }else {
            attrs.attack += roleConf.Attack + Math.floor(role.grouth * 
                            roleConf.AttackGrouth / 100 * role.level);
            attrs.defence += roleConf.Defence + Math.floor(role.grouth * 
                            roleConf.DefenceGrouth / 100 * role.level);
            attrs.mdefence += roleConf.MagicDefence + Math.floor(role.grouth * 
                            roleConf.MagicDefenceGrouth / 100 * role.level);
        }

        attrs.health += gConfRoleLevel[role.level]['Soldier' + soldierKind];

        // 科技加成
        var tech = this.user.tech;
        for( var techId in tech ) {
            var techLevel = tech[techId];
            var techConf = gConfTech[techId];
            if( techConf.Soldier == soldierKind ) {
                attrs[techConf.Attribute] += techConf['Value'+techLevel];
            }
        }

        // 装备的属性
        var equips = [role.weapon, role.armor, role.accessory];
        var gemFactor = 0; //战斗力计算因子
        var equipStarFactor = 0;
        // 转生 洗髓 
        var equipRefineAttrs = {
            'attack': 0, 
            'defence': 0,
            'mdefence': 0, 
            'health': 0,
            'fortitude': 0,
            'hit': 0,
            'unblock': 0,
            'block' : 0,
            'critdamage': 0,
            'miss': 0,
        };
        for(var i=0; i<3; i++ ) {
            var equip = this.user.equip[equips[i]];
            if( !equip ) {
                continue;
            }
            
            var type = equip.type;
            var firstAttrChanges = [];
            var secondAttrChange = '';
            var equipConf = null;
            if( type == 'weapon' || type == 'magic' ) {
                equipConf = gConfWeapon;
                firstAttrChanges = ['attack'];
                secondAttrChange = 'unblock';
            }else if( type == 'armor' ) {
                equipConf = gConfArmor;
                firstAttrChanges = ['defence','mdefence'];
                secondAttrChange = 'miss';
            }else if( type == 'accessory' ) {
                equipConf = gConfAccessory;
                firstAttrChanges = ['health'];
                secondAttrChange = 'hit';
            }

            var value = equipConf[equip.strength]['Purple'+equip.level];

            for( var j=0; j<firstAttrChanges.length; j++ ) {
                var change = firstAttrChanges[j];
                attrs[change] += value;
            }

            // 宝石
            for( var slot in equip.gems ) {
                var gid = equip.gems[slot];
                var gemConf = gConfGem[gid];
                if( !gemConf ){
                    continue; 
                }

                gemFactor += gemConf.FightForceFactor;
                if( gemConf.Key == 'magic' ) {
                    attrs.attack += gemConf.Value;
                }else{
                    attrs[gemConf.Key] += gemConf.Value;
                }
            }
            
            if( equip.star > 0 ) {
                var equipStarConf = gConfEquipStar[equip.star];

                // 星级
                if( equip.type == 'weapon' || equip.type == 'magic') {
                    attrs.critdamage += equipStarConf.Critdamage;
                    attrs.hit += equipStarConf.Hit;
                }else if( equip.type == 'armor' ) {
                    attrs.block += equipStarConf.Block;
                    attrs.miss += equipStarConf.Miss;
                }else if( equip.type == 'accessory' ) {
                    attrs.fortitude += equipStarConf.Fortitude;
                    attrs.unblock += equipStarConf.Unblock;
                }

                equipStarFactor += equipStarConf.StarFactor;
            }

            // 转生 洗髓 
            if( equip.refine_level ) {
                var equipRefine = equip.refine;
                var equipRefineLevel = equip.refine_level;
                var equipRefineConf = gConfEquipRefine[equipRefineLevel];
                
                for( var attr in equipRefine ) {
                    var equipType = equip.type;
                    if( equipType == 'magic' ) equipType = 'weapon';
                    var equipUpgrageAdd = equipRefineConf[equipType.capWord() + attr.capWord()];
                    var equipRefineAdd = equipRefine[attr];
                    attrs[attr] += equipUpgrageAdd; 
                    attrs[attr] += equipRefineAdd; 
                    equipRefineAttrs[attr] += equipUpgrageAdd + equipRefineAdd; 
                }
            }
        }
        
        gemFactor = gemFactor / 1000;
        // 装备转生第二属性增加的战斗力
        var equipRefineFightForce = 0;
        for( var attr in equipRefineAttrs ) {
            if( (attr == 'attack') || (attr == 'defence') 
                || (attr == 'mdefence') || (attr == 'health') )  continue;
            
            var equipRefineFightForceFactor = 6;
            if( (attr == 'block') || (attr == 'unblock') ) {
                equipRefineFightForceFactor = 12;
            }
            
            var equipRefineFactor = 0;
            if((attr == 'critdamage') || (attr == 'hit')) {
                equipRefineFactor =  equipRefineAttrs.attack;
            }else if((attr == 'block') || (attr == 'miss')) {
                equipRefineFactor =  (equipRefineAttrs.defence + equipRefineAttrs.mdefence) / 2 ;
            }else if((attr == 'unblock') || (attr == 'fortitude')) {
                equipRefineFactor =  equipRefineAttrs.health;
            }

            var equipRefineAttrAdd = equipRefineAttrs[attr];
            equipRefineFightForce += Math.floor(equipRefineFactor * ((equipRefineAttrAdd * 0.5 / (equipRefineAttrAdd + 7000)) / equipRefineFightForceFactor));
        }

        // 技能加成
        var skillsFactor = 0;   //战斗力计算因子
        var skills = role.skill_levels;
        for (var skill in skills) {
            var level = skills[skill];
            var skillKind = gGetSkillKind(soldierKind, skill);

            skillsFactor += level*(gConfSkill[skillKind][skill].FightForceFactor);

            if (gIsStandSkill(skill)) {
                var skillEffectConf = gConfSkillEffect[skillKind][skill][level];
                // 被动技能并且不是红颜
                if (2 == skillEffectConf.Type && 5 != soldierKind) {
                    attrs[skillEffectConf.Effect] += skillEffectConf.Param;
                }
            }
        }
        
        // 炼魂加成
        if( role.soul ) {
            var soulConf = gConfSoul[role.soul];
            attrs.attack += soulConf.Attack;
            attrs.defence += soulConf.Defence;
            attrs.mdefence += soulConf.Mdefence;
        }

        // 突破加成
        if( role.grouth_break ) {
            var breakConf = gConfGrouthBreak[role.grouth_break];
            attrs.attack += breakConf.Attack;
            attrs.defence += breakConf.Defence;
            attrs.mdefence += breakConf.MDefence;
        }
        
        if( role.soul_break ) {
            var breakConf = gConfSoulBreak[role.soul_break];
            attrs.attack += breakConf.Attack;
            attrs.defence += breakConf.Defence;
            attrs.mdefence += breakConf.MDefence;
        }


        // 军团科技加成
        var leagueTech = this.user.league.tech || {};
        for( var techId in leagueTech ) {
            var techLevel = leagueTech[techId];
            var leagueTechConf = gConfLeagueTech[techId][techLevel];
            attrs[leagueTechConf.Attribute] += leagueTechConf.Value;
        }

        // 神将加成
        if( role.god ) {
            var godAdd = (gConfGodLevel[role.god].AttrAdd/100 + 1);
            attrs.attack = parseInt(attrs.attack * godAdd);
            attrs.defence = parseInt(attrs.defence * godAdd);
            attrs.mdefence = parseInt(attrs.mdefence * godAdd);
        }
        
        // 神兽技能加成 
        var petFactor = 0;
        var pet = this.user.pet;
        if( (this.user.status.level >= gConfGlobal.PetOpenLevel) && pet ) {
            var petAttrs = {
                'attack': 0, 
                'defence': 0,
                'mdefence': 0, 
                'health': 0,
                'fortitude': 0,
                'hit': 0,
                'unblock': 0,
                'block' : 0,
                'critdamage': 0,
                'miss': 0,
            };
            attrs.health += gConfPet[pet.level].Health;
            var petSkillLevels = pet.skill_levels;
            for( var petSkillId in petSkillLevels ) {
                var petSkillLevel = petSkillLevels[petSkillId];
                var petSkillEffectConf = gConfPetSkillEffect[petSkillId][petSkillLevel]; 
                petFactor += petSkillEffectConf.FightForceFactor;
                if( 2 == petSkillEffectConf.Type ) {
                    // 神兽被动技能 
                    if( 'magic' == petSkillEffectConf.Effect ) {
                        if( 4 == soldierKind || 5 == soldierKind ) {
                            petAttrs['attack'] +=  petSkillEffectConf.Param;
                        }

                        continue;
                    }

                    if( 'attack' == petSkillEffectConf.Effect ) { 
                        if ( 1 == soldierKind || 2 == soldierKind || 3 == soldierKind ) {
                            petAttrs['attack'] +=  petSkillEffectConf.Param;
                        }

                        continue;
                    }
                    
                    petAttrs[petSkillEffectConf.Effect] +=  petSkillEffectConf.Param;
                }
            } 
            
            var petBuff = pet.buff || 0;
            for( var name in petAttrs ) {
                attrs[name] += Math.floor(petAttrs[name] * (1 + gConfGlobal.PetBuffAddAttribute * petBuff/100));
            }
        } 
        
        // 坐骑加成
        if (role.ride) {
            var userRide = this.user.ride;
            var roleRide= role.ride;

            // 马匹加成
            var horse = userRide.horse[roleRide.horse];
            if (horse) {
                var rideAttr = {
                    'attack' : 0,
                    'defence' : 0,
                    'mdefence' : 0,
                    'health' : 0,
                };

                var horseConf = gConfHorse[horse.horse_id];
                var horseLevelFactor = gConfHorseLevel[horse.level].Factor/100 + 1;

                rideAttr.attack += parseInt(horseConf.Attack * horseLevelFactor);
                rideAttr.defence += parseInt(horseConf.Defence * horseLevelFactor);
                rideAttr.mdefence += parseInt(horseConf.MagicDefence * horseLevelFactor);
                rideAttr.health += parseInt(horseConf.Health * horseLevelFactor);

                // 有缘马匹攻击力加成
                if( horse.horse_id == roleConf.Horse ) {
                    rideAttr.attack += roleConf.HorseAttack;
                }

                // 马鞍加成
                var seatConf = gConfHarness.seat;
                var seatLevelFactor = gConfHarnessLevel[roleRide.seat].Factor/100 + 1;

                rideAttr.attack += parseInt(seatConf.Attack * seatLevelFactor);
                rideAttr.defence += parseInt(seatConf.Defence * seatLevelFactor);
                rideAttr.mdefence += parseInt(seatConf.MagicDefence * seatLevelFactor);
                rideAttr.health += parseInt(seatConf.Health * seatLevelFactor);
                // 马鞍达到等级后的额外加成
                if( roleRide.seat >= seatConf.LevelLimit ) {
                    rideAttr.mdefence += seatConf.ExtraMagicDefence;  
                }

                // 马鞭加成
                var whipConf = gConfHarness.whip;
                var whipLevelFactor = gConfHarnessLevel[roleRide.whip].Factor/100 + 1;

                rideAttr.attack += parseInt(whipConf.Attack * whipLevelFactor);
                rideAttr.defence += parseInt(whipConf.Defence * whipLevelFactor);
                rideAttr.mdefence += parseInt(whipConf.MagicDefence * whipLevelFactor);
                rideAttr.health += parseInt(whipConf.Health * whipLevelFactor);
                // 马鞭达到等级后的额外加成
                if( roleRide.whip >= whipConf.LevelLimit ) {
                    rideAttr.defence += whipConf.ExtraDefence;  
                }

                attrs.attack += parseInt(rideAttr.attack);
                attrs.defence += parseInt(rideAttr.defence);
                attrs.mdefence += parseInt(rideAttr.mdefence);
                attrs.health += parseInt(rideAttr.health);
            }
        }

        // 羽翼加成 
        if( role.wing ) {
           var wingConf = gConfWing[role.wing]; 
           attrs.attack += parseInt(wingConf.Attack);
           attrs.defence += parseInt(wingConf.Defence);
           attrs.mdefence += parseInt(wingConf.Mdefence);
        }

        // 升星加成
        var starFactor = 0;
        if (role.star) {
            var starConf = gConfRoleStar[soldierKind][role.star];
            attrs.attack += parseInt(starConf.Attack);
            attrs.defence += parseInt(starConf.Defence);
            attrs.mdefence += parseInt(starConf.MDefence);

            var extraType = starConf.ExtraType;
            var extraValue = starConf.ExtraValue;
            if (extraType && extraValue) {
                var attrName = gRoleAttrNameMap[extraType];
                if (attrName) {
                   attrs[attrName] += parseInt(extraValue); 
                }
            }

            starFactor += +starConf.FightForceFactor;
        }

        // 星座加成
        var horoscopeFactor = 0;
        var horoscope = role.horoscope;
        if (horoscope) {
            var horoscopeAttrs = horoscope.attrs;

            var horoscopeAttr = horoscopeAttrs.attr;
            if (horoscopeAttr) {
                gRoleBasicAttrNameArr.forEach(function(attrName) {
                    addAttrValue(attrs, attrName, horoscopeAttr[attrName]||0);
                });
            }

            horoscopeFactor = horoscopeAttrs['fightForceFactor'] || 0;
        }

        // 成就加成
        if (this.user.achievements) {
            var achievementConf = gConfAchieveLevel[this.user.achievements.level];
            attrs.attack += (achievementConf.Attack || 0);
            attrs.defence += (achievementConf.Defence || 0);
            attrs.mdefence += (achievementConf.MagicDefence || 0);
            attrs.health += (achievementConf.Health || 0);
        }

        // 刘乐乐英雄志加成
        if (role.id == 48 && this.user.hero_log) {
            var hero_log_liulele = this.user.hero_log.liulele;
            attrs.attack += (hero_log_liulele.Attack || 0);
            attrs.defence += (hero_log_liulele.Defence || 0);
            attrs.mdefence += (hero_log_liulele.MagicDefence || 0);
            attrs.health += (hero_log_liulele.Health || 0);
        }

        // 计算战斗力
        skillsFactor = skillsFactor/3000 + 1;
        var fightForce = Math.floor( (role.level*5 
            + attrs.health/20  
            + (attrs.attack + attrs.defence + attrs.mdefence)/4) 
            * (skillsFactor + gemFactor + petFactor/5000 + equipStarFactor/1000 + starFactor/1000 + horoscopeFactor/1000) 
            + equipRefineFightForce);

        if( fightForce !=  role.fight_force ){
            role.fight_force = fightForce;
            this.markDirty(util.format('role.%d.fight_force', role.id));
        }
        
        this.roleAttrs[role.id] = attrs;
    },

    roleAddEquip : function(role, equip) {
        var type = equip.type;
        if( type == 'magic' ) {
            type = 'weapon';
        }
        
        role[type] = equip.id;
        this.markDirty(util.format('role.%d.%s', role.id, type));
    },

    roleRemoveEquip : function(role, equip) {
        var type = equip.type;
        if( type == 'magic' ) {
            type = 'weapon';
        }
       
        role[type] = 0;
        this.markDirty(util.format('role.%d.%s', role.id, type));
    },

    addEquip : function(type, level) {
        level = level || 1;
        var equip = {
            'id': this.nextId(),
            'type':type,
            'strength':1,
            'level':level,
            'gems':{},
            'fail':0,
            'refine_level':0,
            'refine':{},
            'star':0,
        };

        this.user.equip[equip.id] = equip;
        this.markDirty('equip.'+equip.id);

        return equip;
    },

    checkRoleEquip : function() {
        // 检查装备,武将数据
        var roles = this.user.role;
        var equips = this.user.equip;
        
        for( var eid in equips ) {
            var equip = equips[eid];
            if( equip.hasOwnProperty('role') ) {
                delete equip.role; 
                this.markDelete(util.format('equip.%d.role', eid));
            }
            if( !equip.hasOwnProperty('star') ) {
                equip.star = 0;
                this.markDirty(util.format('equip.%d.star', eid));
            }
        }
        
        var equipWeard = {};

        for( var rid in roles ) {
            var role = roles[rid];
            if( !role || !role.hasOwnProperty('level') ) {
                // 错误数据
                delete roles[rid];
                this.markDelete('role.'+rid);
                continue;
            }

            if( !role.soul ) {
                role.soul = 0;
            }

            if( !role.god ) {
                role.god = 0;
            }

            if( !role.soul_break ) {
                role.soul_break = 0;
            }

            if( !role.grouth_break ) {
                role.grouth_break = 0;    
            }

            if( role._attr ) {
                // 删除_attr
                delete role._attr;
                this.markDelete(util.format('role.%d._attr', rid));
            }
            
            var equipSlots = ['weapon', 'accessory', 'armor'];
            for( var i=0; i<equipSlots.length; i++ ) {
                var slot = equipSlots[i];
                var equipId = role[slot];
                if( !equipId ) continue;

                if( !equips[equipId] || (equipId in equipWeard) ) {
                    // 装备丢失 or 已经被穿了,修正数据
                    role[slot] = 0;
                    this.markDirty(util.format('role.%d.%s', rid, slot));
                }else{
                    equipWeard[equipId] = rid; 
                }
            }
        }
    },

    getEquipWeard : function() {
        // 获取装备穿戴数据
        var roles = this.user.role;
        var equips = this.user.equip;
        
        var equipWeard = {};

        for( var rid in roles ) {
            var role = roles[rid];
            var equipSlots = ['weapon', 'accessory', 'armor'];
            for( var i=0; i<equipSlots.length; i++ ) {
                var slot = equipSlots[i];
                var equipId = role[slot];
                if( !equipId ) continue;
                
                equipWeard[equipId] = rid; 
            }
        }

        return equipWeard;
    },

    haveSpecialRole : function() {
        for( var rid in this.user.role ) {
            if( gConfRole[rid].ReceiveMode == 5 ) {
                return true;
            }
        }

        return false;
    },

    calculateRoleHoroscopeAttrs: function(role) {
        var horoscope = role.horoscope;
        if (!horoscope) {
            return;
        }

        var rid = role.id;
        var roleConf = gConfRole[rid];
        var soldierKind = roleConf.Soldier;
        var horoscopeId = roleConf.HoroscopeId;
        var horoscopeGridConf = gConfHoroscopeGrid[horoscopeId];
        
        horoscope.attrs = {};
        var attrs = horoscope.attrs;
        var grids = horoscope.grids;

        var gridArr = Object.keys(grids);
        for (var i = 0, len = gridArr.length; i < len; ++i) {
            var gridId = gridArr[i];
            if (!grids[gridId]) continue;

            this._addHoroscopeEffectToAttrs(soldierKind, attrs, horoscopeGridConf[gridId].EffectId);
        }

        this.markDirty(util.format('role.%d.horoscope', rid));
    },

    _addHoroscopeEffectToAttrs: function(soldierKind, attrs, effectId) {
        var effectConf = gConfHoroscopeEffect[effectId];
        if (!effectConf) {
            return;
        }

        var effect = effectConf.Effect;
        switch (effect) {
        case 'Restrain':
            var subAttrs = attrs.restrain;
            if (!subAttrs) {
                subAttrs = {};
                attrs.restrain = subAttrs;
            }
            addAttrValue(subAttrs, effectConf.Param1, effectConf.Param2);
            break;

        case 'Skill':
            var subAttrs = attrs.skill;
            if (!subAttrs) {
                subAttrs = {};
                attrs.skill = subAttrs;
            }

            var skillId = effectConf.Param1;
            var skillKind = gGetSkillKind(soldierKind, skillId);
            var effectName = gConfSkillEffect[skillKind][skillId][1].Effect;

            var attr = subAttrs[effectName];
            if (!attr) {
                attr = {};
                subAttrs[effectName] = attr;
            }

            if (effectConf.Param2) {
                addAttrValue(attr, 'param', effectConf.Param2);
            }
            if (effectConf.Param3) {
                addAttrValue(attr, 'damage', effectConf.Param3);
            }
            break;

        case 'Attribute':
            var subAttrs = attrs.attr;
            if (!subAttrs) {
                subAttrs = {};
                attrs.attr = subAttrs;
            }

            var attrName = gRoleAttrNameMap[effectConf.Param1];
            if (attrName) {
                addAttrValue(subAttrs, attrName, effectConf.Param2);
            }
            break;

        default:
            var subAttrs = attrs.spec;
            if (!subAttrs) {
                subAttrs = {};
                attrs.spec = subAttrs;
            }
            addAttrValue(subAttrs, effect, effectConf.Param2);
            break;
        }
        
        if (effectConf.FightForceFactor) {
            addAttrValue(attrs, 'fightForceFactor', effectConf.FightForceFactor);
        }
    },

    /**************************************************
    临时状态相关
    **************************************************/
    addStatusNotice: function(notice) {
        this.status.notice = notice;
    },

    addStatusAwards: function(awards) {
        if (!awards) return;

        if (this.status.awards) {
            this.status.awards = this.status.awards.concat(awards);
        } else {
            this.status.awards = awards;
        }
    },

    /**************************************************
    包裹相关
    **************************************************/
    addAwards : function(awards) {
        // awards 格式[[user,gold,100], [material,10,10]], [gem,10,1], [horse,1,1],
        //  [equip, weapon, '1',1], [card,1,1], [user, cash, 100], 
        // 返回{'awards':带数量的奖励(material, user等), 'gems':宝石, [horse,马的系统ID,horse对象}] }
        var addedAwards = [];
        for( var i=0,max=awards.length; i<max; i++ ) {
            var award = awards[i];
            if( !award ) continue;

            var awardType = award[0], 
                awardId = award[1], 
                awardNum = +award[2];

            if( awardType == 'user' ) {
                if( awardId == 'xp' ) {
                    this.addXp(awardNum);
                }else{
                    this.alterResource(awardId, awardNum);
                }
                addedAwards.push(award);
            }else if( awardType == 'material' || awardType == 'gem') {
                this.addInventory(awardType, awardId, awardNum);
                addedAwards.push(award);
            }else if( awardType == 'card' ) {
                this.addRoleCard(awardId, awardNum);
                addedAwards.push(award);
            }else if( awardType == 'role' ) {
                for( var j = 0; j<awardNum; j++ ) {
                    if( !this.canEnrollRole(awardId) ) {
                        this.roleToCard(awardId);
                        addedAwards.push(['card', awardId, 1]);
                    }else{
                        addedAwards.push(['role', awardId, this.addRole(awardId)]);
                    }
                }
            }else if( awardType == 'equip') {
                var equipType = award[1], 
                    equipLevel = +award[2], 
                    awardNum = +award[3];
                for( var j = 0; j<awardNum; j++ ) {
                    equip = this.addEquip(equipType, +equipLevel);
                    addedAwards.push(['equip', equip.id, equip]); 
                }
            }else if( awardType == 'horse' ) {
                for( var j = 0; j < awardNum; ++j ) {
                    var uniqId = this.addHorse(+awardId);              
                    addedAwards.push([ 'horse', uniqId, this.user.ride.horse[uniqId] ]);
                }
            }
        }

        return addedAwards;
    },

    addCosts: function(costs) {
        var user = this.user;

        var enough = true;
        for (var i = 0, len = costs.length; i < len; ++i) {
            var cost = costs[i];
            if (!cost) continue;

            var type = cost[0];
            var id = cost[1];
            var num = cost[2];

            switch (type) {
            case 'user':
                if (((user.status[id] || 0) +  num) < 0) {
                    enough = false;
                }
                break;
            case 'material':
                if ((this.getInventory('material', id) + num) < 0) {
                    enough = false;
                }
                break;
            case 'gem':
                if ((this.getInventory('gem', id) + num) < 0) {
                    enough = false;
                }
                break;
            case 'card':
                if (((user.role_card[id] || 0) + num) < 0) {
                    enough = false;
                }
                break;
            default:
                enough = false;
                break;
            }

            if (!enough) break;
        }

        if (!enough) {
            return [];
        }

        for (var i = 0, len = costs.length; i < len; ++i) {
            var cost = costs[i];
            if (!cost) continue;

            var type = cost[0];
            var id = cost[1];
            var num = cost[2];

            switch (type) {
            case 'user':
                this.alterResource(id, num);
                break;
            case 'material':
                this.addInventory('material', id, num);
                break;
            case 'gem':
                this.addInventory('gem', id, num);
                break;
            case 'card':
                this.addRoleCard(id, num);
                break;
            default:
                break;
            }
        }

        return costs;
    },

    checkInventory : function() {
        var timeLimits = [];    //过期的
        var now = common.getTime();
        var material = this.user.inventory.material;
        for( var id in material ) {
            var materialConf = gConfMaterial[id];
            if (!materialConf) {
                ERROR(util.format('material %d not exist', id)); 
                continue;
            }
            if( gConfMaterial[id].EndTime && (now > gConfMaterial[id].EndTime) ) {
                timeLimits.push(id);
            }
        }

        for( var i=0; i<timeLimits.length; i++ ) {
            var id = timeLimits[i];
            material[id] = 0;
            this.markDirty('inventory.material.'+id);
        }
    },

    addInventory : function(rtype, rid, num){
        var inventory = this.user.inventory;
        if (!(rtype in inventory)) {
            inventory[rtype] = {}; 
        }

        if (!(rid in inventory[rtype])) {
            inventory[rtype][rid] = 0;
        }

        var oldNum = inventory[rtype][rid];
        var changes = {};
        if (num > 0) {
            changes[rid] = num;
            this._recordItem(rtype, rid, num, oldNum);
        } else {
            if (this.getInventory(rtype, rid) + num < 0) {
                return false;
            }

            if ('material' == rtype) {
                var materialConf = gConfMaterial[rid];
                var equalId = materialConf.EqualId;
                if (equalId) {
                    var equalNum = inventory[rtype][equalId];
                    if (equalNum > 0) {
                        var usedEqualNum = 0;
                        if (materialConf.EqualLevel) { // 优先使用等价道具
                            if (equalNum + num >= 0) {
                                usedEqualNum = num;
                                num = 0;
                            } else {
                                usedEqualNum = -equalNum;
                                num += equalNum;
                            }
                        } else {
                            var thisNum = inventory[rtype][rid];
                            if (thisNum + num < 0) {
                                usedEqualNum = thisNum + num;
                                num = -thisNum;
                            }
                        }
                        
                        if (usedEqualNum < 0) {
                            changes[equalId] = usedEqualNum;
                            this._recordItem(rtype, rid, usedEqualNum, equalNum);
                        }
                    }
                }
            }

            if (num < 0) {
                changes[rid] = num;
                this._recordItem(rtype, rid, num, oldNum);
            }
        }

        for (var id in changes) {
            inventory[rtype][id] += changes[id];
            this.markDirty(util.format('inventory.%s.%d', rtype, id));
        }
        
        return changes;
    },

    _recordItem : function(rtype, rid, num, oldNum) {
        var user = this.user;
        if (num > 0) {
            //AddItem|iEventId|iWorldId|iUin|dtEventTime|iRoleId|vRoleName|iJobId|iGender|
            //iRoleLevel|iItemId|iItemType|iBeforeNum|iAfterNum|iItemNum|iItemGuid|iProductType
            writeRecord(util.format('AddItem|%s|%d|%s|%s|%d|RoleName|0|0|%d|%d|%s|%d|%d|%d|0|0',
                this.action.getEvent(), config.ServerId, this.openid, Date.stdFormatedString(), this.uid,
                user.status.level, rid, rtype, oldNum, oldNum + num, num));
        } else {
            //UseItem|iEventId|iWorldId|iUin|dtEventTime|iRoleId|vRoleName|iJobId|iGender|
            //iRoleLevel|iItemId|iItemType|iBeforeNum|iAfterNum|iItemNum|iItemGuid
            writeRecord(util.format('UseItem|%s|%d|%s|%s|%d|RoleName|0|0|%d|%d|%s|%d|%d|%d|0',
                this.action.getEvent(), config.ServerId, this.openid, Date.stdFormatedString(), this.uid,
                user.status.level, rid, rtype, oldNum, oldNum + num, -num));
        }
    },

    getInventory : function(rtype, rid, onlyThis) {
        var counts = this.user.inventory[rtype];
        if (!counts) {
            return 0;
        }

        var num = counts[rid] || 0;

        if (!onlyThis && 'material' == rtype) {
            var materialConf = gConfMaterial[rid];
            if (materialConf.EqualId) {
                num += (counts[materialConf.EqualId] || 0);
            }
        }

        return num;
    },

    doDailyTask : function(id) {
        var exist = this.user.daily_task[id] || 0;
        if( exist >= gConfDailyTask[id].Target ) return;

        this.user.daily_task[id] = exist+1;
        this.markDirty('daily_task.'+id);

        this.triggerInsurgency();
    },

    getDailyTaskScore : function() {
        var score = 0;
        var daily_task = this.user.daily_task;
        for( var id in daily_task ) {
            var conf = gConfDailyTask[id];
            if( daily_task[id] >= conf.Target ) {
                score += conf.Score;
            }
        }

        return score;
    },

    doTask : function( type, arg ) {
        this.doingTasks.push([type, arg]);
    },

    checkTask : function() {
        var finishedTasks = [];
        var user = this.user;
        var status = user.status;
        var taskDirty = false;
        
        var tasks = [];
        if( user.task.main.id ) {
            tasks.push(this.user.task.main);
        }
        
        /*
        if( user.task.branch.id ) {
            tasks.push(this.user.task.branch);
        }
        */
        
        for( var i=0,max=tasks.length; i<max; i++ ) {
            var task = tasks[i];
            var taskConf = gConfTask[task.id];
            if( !taskConf ) {
                continue;
            }

            var isFinished = true;

            for( var j=1; j<=2; j++ ) {
                var taskType = taskConf['TaskType'+j];
                var taskArg = taskConf['TaskArg'+j];
                var target = taskConf['TaskTarget'+j];
                var progress = task.progress[j];
                if( !taskType ) {
                    continue; 
                }

                if( taskType in countingTaskTypes ) {
                    // 执行任务数据更新
                    for( var k = 0, maxk = this.doingTasks.length; k < maxk; k++) {
                        var type = this.doingTasks[k][0];
                        var arg = this.doingTasks[k][1];
                        if( taskType != type ) {
                            continue;
                        }

                        if( progress >= target ) {
                            continue;
                        }

                        if( taskArg == 'all' || taskArg == arg ){
                            progress += 1;
                            task.progress[j] = progress;
                            taskDirty = true;
                        }
                    } 

                    // 判断计数型任务完成情况
                    if( progress < target ) {
                        isFinished = false;
                    }
                }else {
                    // 判断拥有型任务完成情况
                    if( !isFinished ) {
                        continue; 
                    }
                    
                    isFinished = checkNonCountingTask(this, taskType, taskArg, target);
                }
            }
                
            // 完成任务奖励,更新新任务
            if( isFinished ) {
                taskDirty = true;

                //add gold Xp awards  
                var addGold = taskConf.Gold
                var addXp = taskConf.Xp;

                var awards = [];
                var award = taskConf.Award1;
                if( award ) {
                    awards.push(award);
                }

                status.gold += addGold;
                this.addXp(addXp);
                awards = this.addAwards(awards);

                finishedTasks.push({'id':task.id,'gold':addGold,'xp':addXp,'level': status.level,'awards':awards});

                var newTaskId = task.id + 1;
                if( gConfTask[newTaskId] ) {
                    task.id = newTaskId;
                    task.progress[1] = 0; 
                    task.progress[2] = 0;
                }else{
                    task.id = 0;
                }
            }
        }

        if( finishedTasks.length > 0 ) {
            this.markDirty('status.gold');
        }

        if( taskDirty ) {
            this.markDirty('task');
        }

        return finishedTasks;
    },

    getAchievements : function(checkUpdate) {
        var user = this.user;
        if (user.status.level < gConfGlobal.AchievementOpenLevel) {
            return;
        }

        if (!user.achievements) {
            user.achievements = {
                level:0,
                score:0,
                goals:{},
            }

            this.markDirty('achievements.level');
            this.markDirty('achievements.score');
            checkUpdate = true;
        }

        if (checkUpdate) {
            var goals = user.achievements.goals;
            for (var id in gConfAchieveGoal) {
                var goal = goals[id];
                if (!goal) {
                    goal = {level:1, value:0};
                    this.statAchievementGoal(id, goal);
                    goals[id] = goal;
                    this.markDirty('achievements.goals.'+id);
                }
            }
        }

        return user.achievements;
    },
    
    statAchievementGoal : function(id, goal) {
        var goalConf = gConfAchieveGoal[id][goal.level];
        if (!goalConf) {
            return 0;
        }

        var eventClass = questEvent[goalConf.GoalEvent];
        goal.value = eventClass.stat(this.user, goalConf);
        if (goal.value >= goalConf.GoalTarget) {
            this.finishedAchievements.push(id);
        }
    },

    checkEvent : function(eventId, eventArg) {
        var user = this.user;

        //检测成就
        var achievements = this.getAchievements();
        if (achievements) {
            eventAchievements = gConfRuntime.AchievementEventMap[eventId];
            var eventClass = questEvent[eventId];

            for (var id in eventAchievements) {
                var goal = achievements.goals[id];
                if (!goal) {
                    continue;
                }

                var goalConf = gConfAchieveGoal[id][goal.level];
                if (!goalConf || !eventClass.matching(user, goalConf, eventArg)) {
                    continue;
                }
                
                if (goal.value >= goalConf.GoalTarget) {
                    continue;
                }
                
                var oldValue = goal.value;
                goal.value = eventClass.getValue(user, goal.value, goalConf, eventArg);
                if (goal.value > oldValue) {
                    this.markDirty('achievements.goals.' + id);
                    if (goal.value >= goalConf.GoalTarget) {
                        this.finishedAchievements.push(id);
                    }
                }
            }
        }
    },

    updateLeagueWarEnergy : function(){
        var now = common.getTime();

        var league_war = this.user.league_war;
        var passedHour = Math.floor((now - league_war.energy_time)/3600);
        if( passedHour < 1 ) return;
        var energy = Math.floor(passedHour * gConfGlobal.LeagueWarEnergyPerHour);
        league_war.energy += energy;
        league_war.energy_time += energy / gConfGlobal.LeagueWarEnergyPerHour * 3600;

        this.markDirty('league_war.energy');
        this.markDirty('league_war.energy_time');
    },

    addGradeXp : function(xp){
        if( xp < 0 ) return;

        var league_war = this.user.league_war;
        var oldLevel = league_war.grade_level;
        league_war.grade_xp += xp;
        
        var newLevel = 1;
        for( ; newLevel<100; newLevel++ ) {
            if( !gConfGradeLevel[newLevel+1] || gConfGradeLevel[newLevel].Xp > league_war.grade_xp ) {
                break;
            }
        }
        if( !gConfGradeLevel[newLevel+1] ) {
            // 达到最大等级
            var maxXp = gConfGradeLevel[newLevel].Xp - 1;
            if( league_war.grade_xp > maxXp ) {
                league_war.grade_xp = maxXp; 
            }
        }
        
        if( newLevel != oldLevel ) {
            league_war.grade_level = newLevel;
            this.markDirty('league_war.grade_level');
        }
        
        if( xp > 0 ) {
            this.markDirty('league_war.grade_xp');
        }
    },

    updateBattleWorldEnergy : function(){
        var now = common.getTime();

        var battleWorld = this.user.battle_world;
        var energyOutputHours = gConfGlobal.BattleWorldEnergyOutputHours;
        var passedHour = Math.floor(((now - battleWorld.energy_time) / 3600) / energyOutputHours) * energyOutputHours ;
        if( passedHour < energyOutputHours ) return;
        var energy = Math.floor(passedHour * gConfGlobal.BattleWorldEnergyPerHour);
        battleWorld.energy += energy;
        battleWorld.energy_time += passedHour * 3600;

        this.markDirty('battle_world.energy');
        this.markDirty('battle_world.energy_time');
    },

    checkBattleWorldGuard: function(score) {
        var battleWorld = this.user.battle_world;
        if (!battleWorld) {
            return true;
        }    

        if (gConfGlobal.BattleWorldGuardScore) {
            var nowStamp = Date.getStamp();
            if (!battleWorld.hasOwnProperty('guardStamp') || (nowStamp - battleWorld.guardStamp >= 600)) {
                battleWorld.guardScore = 1; 
                battleWorld.guardStamp = nowStamp;
                this.markDirty('battle_world.guardScore');
                this.markDirty('battle_world.guardStamp');
                this.mergeDirty();
                return true;
            }

            if (battleWorld.guardScore >= gConfGlobal.BattleWorldGuardScore) {
                return false;
            } 

            var addScore = score || 1;
            if (battleWorld.secondStamp == nowStamp) {
                battleWorld.secondScore += 1;
                if (battleWorld.secondScore > 2) {
                    addScore += (4*battleWorld.secondScore - 11);
                }
            } else {
                battleWorld.secondStamp = nowStamp;
                battleWorld.secondScore = 1;
            }

            battleWorld.guardScore += addScore;
            this.markDirty('battle_world.guardScore');
            this.mergeDirty();
        }    
     
        return true;
    },
 
    getVip: function() {
        var oldVip = this.user.status.vip;
        var paid = this.user.activity.paid;
        for( i=1; i<1000; i++ ) {
            if( !gConfVip[i] || gConfVip[i].Cash > paid ) {
                break;
            }
        }
        
        var nowVip = i - 1;
        if( nowVip > oldVip ) {
            this.user.status.vip = nowVip;
            this.markDirty('status.vip');
        }
    },

    saveCashCost: function(cash) {
        this.addDailyVar('cost_cash', cash);

        var activitySchedule = findActivitySchedule('costgift');
        if( activitySchedule && this.user.activity.cost_gift ) { 
            this.user.activity.cost_gift.cash += cash;
            this.markDirty('activity.cost_gift.cash');
        }

        var activityThinksBack = this.getActivity('thanksback', {alias:true});
        if (activityThinksBack) {
            activityThinksBack.cash += cash;
            this.saveActivityValue('thanksback', 'cash', {alias:true});
        }

        var activitySchedule = findActivitySchedule('yearfeedback');
        if( activitySchedule && this.user.activity.year_feed_back && 
            this.user.status.level >= gConfActivities.yearfeedback.OpenLevel ) { 
            this.user.activity.year_feed_back.cash += cash;
            this.markDirty('activity.year_feed_back.cash');

            var worldReq = {
                uid: this.uid,
                mod: 'activity',
                act: 'update_year_feed_back',
                args: {
                    'cash': cash,
                }
            };

            var worldResp = { };
            requestWorld(worldReq, worldResp);
        }

        var activityNewRedPacket = findActivitySchedule('newredpacket');
        if (activityNewRedPacket && this.user.activity.new_red_packet) {
            this.user.activity.new_red_packet.cash += cash;
            this.markDirty('activity.new_red_packet.cash');
        }
    },

    dailyPayIsOpen : function() {
        if (this.user.status.level < gConfGlobal.DailyPayOpenLevel) {
            return false;
        }

        var now = Date.getStamp();    
        if (common.getDayPassed(gConfGlobalServer.ServerStartTime) < gConfGlobal.DailyPayOpenDays) {
            return false;
        }

        if (gConfGlobal.DailyPayEndTime && now > gConfGlobal.DailyPayEndTime) {
            return false;
        }

        return true;
    },

    // 初始化活动, 不要直接调用, callback(key, activity)
    _initActivity: function(key, schedule, callback) {
        var activity = null;
        var confId = schedule.conf;
        var nowStamp = Date.getStamp();

        switch (key) {
            case 'thanks_back' :                    // 感恩回馈
                activity = {
                    'cash' : 0,                         // 消费元宝
                    'got' : {},                         // 已经领取
                }
                break;
            case 'returnlevelup':                   // 重返三国
                activity = {
                    old_level: this.user.status.level,  // 活动前玩家等级
                    login_days: 0,                      // 活动期间登录天数
                    return_award: 0,                    // 重返奖励领取标志
                    login_award: 0,                     // 登录奖励领取标志
                    leave_days: 0,                      // 玩家离开天数
                }
                break;
            case 'newservergift':                   // 新服礼包
                activity = {
                    level_award: 0,                     // 达到等级的领奖标志(0/1)
                }
                break;
            case 'godgift':                         // 觉醒福利
                activity = {                        
                    times_count: 0,                     // 活动期间的觉醒次数
                    times_award: 0,                     // 可领取觉醒奖励次数
                    count_award: 0,                     // 已经觉醒武将领奖标志(0,1,3,5,7,10)
                    server_award: 0,                    // 全服觉醒奖励标志(0/1)
                }
                break;
            case 'luckycharge':                     // 充值幸运转
                activity = {
                    score: 0,
                    throw_times:0,
                    refresh_times:0,
                    cur_throw_times:0,
                    cur_multiple:1,
                    items:[],
                    last_pos:0,
                }

                var conf = gConfAvLuckyCharge[confId];
                for (var pos in conf) {
                    var posConf = conf[pos];
                    var weights = posConf.ItemWeights;
                    if (posConf.ExistCard) {
                        weights = clone(weights);
                        for (var id in weights) {
                            var itemAwards = gConfAvLuckyChargeItem[confId][id].Award1;
                            if (itemAwards && 'card' == itemAwards[0] && !(itemAwards[1] in this.user.role)) {
                                weights[id] = 0;
                            }
                        }
                    }
                    activity.items.push(common.wRand(weights));
                }
                break;
            case 'worshipsaint':                    // 参拜武圣
                activity = {
                    saint_level: 1,
                    saint_time: 0,
                }

                var conf = gConfAvWorshipSaintLevel[1];
                activity.saint_time = nowStamp + conf.NeedTime;
                break;
            case 'imperialexamination':             // 御前科举
                activity = {
                    round: 0,
                    correctStreak: 0,
                    correct: 0,
                    mistake: 0,
                    score: 0,
                    token: 0,
                    question: 0,
                }
                break;
            case 'shotmearrow':
                activity = {
                    freeShotNum: 0,
                    freeShotTime: 0,
                    shotNum: 0,
                    shotPoint: 0,
                    shotTenNum: 0,

                    dayRewardTime: 0, // 每日奖励领取时间
                    dayRewardArr: {}, // 每日奖励领取情况
                    rankAwardTime: 0 // 排名奖励领取时间
                };
                break;
            case 'daymonthpay':
                activity = {
                    cash: 0,            //活动期间充值元宝数
                    rewardMark: {},     //奖励领取标记
                };
                break;
            case 'treasuretravel':
                activity = {
                    score: 0,           // 积分
                    levelAward: 0,      // 奖励领取等级
                    curPos: 1,          // 当前位置
                };
                break;
            case 'newyearfirstpay':
                activity = {
                    cash: 0,
                    got: 0,
                    day: common.getDate(),
                };
                break;
            case 'newyearsign':
                activity = {
                    award: 0,
                    signDays: {}, 
                };
                break;
            default:
                activity = {};
        }
        
        activity.time = schedule.time;
        if (callback) {
            callback(key, activity);
        }

        this.user.activity[key] = activity;
        for (attr in activity) {
            this.markDirty(util.format('activity.%s.%s', key, attr));
        }

        return activity;
    },

    _checkActivity: function(key, activity) {
        switch (key) {
            case 'imperialexamination':
                var round = 0;
                var firstTime = Date.createFromStamp(activity.time);
                firstTime.setFloatTime(gConfGlobal.ImperialexaminationTimeStart, 0, 0);
                var nowTime = new Date();
                var round = Math.ceil((nowTime - firstTime)/86400000);
                if (round != activity.round) {
                    activity.round = round;
                    activity.correct = 0;
                    activity.mistake = 0;
                    activity.question = 0;

                    this.markDirty(util.format('activity.%s.round', key));
                    this.markDirty(util.format('activity.%s.correct', key));
                    this.markDirty(util.format('activity.%s.mistake', key));
                    this.markDirty(util.format('activity.%s.question', key));
                }

                var startTime = new Date();
                var endTime = new Date();
                startTime.setFloatTime(gConfGlobal.ImperialexaminationTimeStart, 0, 0);
                endTime.setFloatTime(gConfGlobal.ImperialexaminationTimeStart + gConfGlobal.ImperialexaminationTimeLast, 0, 0);
                if (nowTime < startTime || nowTime > endTime) {
                    activity.question = 0;
                    this.markDirty(util.format('activity.%s.question', key));
                }

                break;
            case 'shotmearrow':
                var now = new Date();
                if ((activity.dayRewardTime != 0)
                    && (now.getDayPassed(activity.dayRewardTime) != 1)) {
                    // 数据更新不是今天的
                    activity.dayRewardTime = 0;
                    activity.dayRewardArr = {};
                    this.saveActivityValue(key, 'dayRewardTime');
                    this.saveActivityValue(key, 'dayRewardArr');
                }
                if ((activity.freeShotTime != 0)
                    && (now.getDayPassed(activity.freeShotTime) != 1)) {
                    // 免费借箭数据不是今天的
                    activity.freeShotTime = 0;
                    activity.freeShotNum = 0;
                    this.saveActivityValue(key, 'freeShotTime');
                    this.saveActivityValue(key, 'freeShotNum');
                }
                if (isNaN(activity.shotPoint)) activity.shotPoint = 0;
                break;
            case 'newyearfirstpay':
                var today = common.getDate();
                if (activity.day != today) {
                    activity.cash = 0;
                    activity.got = 0;
                    activity.day = today;
                    this.saveActivityValue(key, 'cash');
                    this.saveActivityValue(key, 'got');
                    this.saveActivityValue(key, 'day');
                }
            default:
                break;
        }
    },

    /*
    desc    :获取活动的每日变量
    */
    getActivityDailyVars: function(key) {
        var vars = {};
        switch (key) {
        case 'worshipsaint':
            return this.getDailyVars('pay_cash', 'worshipsaint_award', 'worshipsaint_times');
        case 'treasuretravel':
            return this.getDailyVars('treasuretravel_times');
        default:
            break;
        }
    },

    /*
    desc    :获取活动信息
    param   :key,活动名称
            :options,选项
                alias, 解析活动别名
                conf, 包含活动配置信息
                delay, 0:不允许delay, 1:允许delay 2:仅允许delay
                onInit, 初始化活动数据时的回调函数
    */
    getActivity: function(key, options) {
        var schedule = findActivitySchedule(key, options && options.delay);
        if (!schedule || this.user.status.level < schedule.open_level) {
            return null;
        }

        if (options && options.alias) {
            var specInfo = gSpecActivitys[key];
            if (specInfo && specInfo.alias) {
                key = specInfo.alias;
            }
        }

        var activity = this.user.activity[key];
        if (!activity || activity.time != schedule.time) {
           activity = this._initActivity(key, schedule, options && options.onInit);
        }

        this._checkActivity(key, activity);
        if (options && options.conf) {
            return {'conf':schedule, 'data':activity};
        } else {
            return activity;
        }
    },

    getActivityOnlyDelay: function(key, options) {
        if (options) {
            options.delay = 2;
        } else {
            options = {delay: 2};
        }
        return this.getActivity(key, options);
    },

    getActivityWithDelay: function(key, options) {
        if (options) {
            options.delay = 1;
        } else {
            options = {delay: 1};
        }
        return this.getActivity(key, options);
    },

    saveActivityValue: function(key, attr, options) {
        if (options && options.alias) {
            var specInfo = gSpecActivitys[key];
            if (specInfo && specInfo.alias) {
                key = specInfo.alias;
            }
        }
        this.markDirty(util.format('activity.%s.%s', key, attr));
    },

    addActivityValue: function(key, attr, value) {
        var activity = this.getActivity(key);
        if (!activity) {
            return null;
        }
        
        var newValue = addAttrValue(activity, attr, value);
        this.saveActivityValue(key, attr);
        return newValue;
    },

    setActivityValue: function(key, attr, value) {
        var activity = this.getActivity(key);
        if (!activity) {
            return;
        }
       
        activity[attr] = value;
        this.saveActivityValue(key, attr);
    },

    // 增加全服活动值, onHandled(world:activity)
    addWorldActivityValue: function(key, time, attr, value, onHandled) {
        var worldReq = {
            mod : 'activity',
            act : 'add_activity_value',
            args : {
                'key' : key,
                'time' : time,
                'attr' : attr,
                'value' : value,
            },
        }
        var worldResp = {};

        requestWorld(worldReq, worldResp, function() {
            var worldActivity = worldResp.data[key];
            onHandled && onHandled(worldActivity);
        });
    },

    // 获取全服活动信息, onHandled(world:activity)
    getWorldActivity: function(key, time, onHandled) {
        var worldReq = {
            mod: 'activity',
            act: 'get_standard_activity_info',
            args: {
                key: key,
                time: time,
            }
        }
        var worldResp = {};

        requestWorld(worldReq, worldResp, function() {
            var worldActivity = worldResp.data[key];
            onHandled && onHandled(worldActivity);
        });
    },

    updateDailyPay : function() {
        var today = common.getDate();
        var dailyPay = this.user.daily_pay;

        if( today == dailyPay.day ) {
            return;
        }

        //更新
        dailyPay.day = today;
        dailyPay.cash = 0;
        dailyPay.reward = 0;

        this.markDirty('daily_pay');
    },

    updateDailyPayMonth : function(force) {
        var today = common.getDate();
        var strToday = common.getDateString();
        var dailyPay = this.user.daily_pay;
        if( !force ) {
            if( !dailyPay.month || common.getDayDiff(dailyPay.month, strToday) >= 30 ) {
                force = true;
            }
        }
        if( !force ) return;

        dailyPay.month = strToday;
        dailyPay.month_pay = 0;
        if (today != dailyPay.month_day) {
            dailyPay.month_day = 0;
        }

        this.markDirty('daily_pay');
    },

    // 检查充值嘉年华是否开启
    checkPayCarnival : function() {
        var nowDate = new Date();
        if (nowDate.getDayPassed(gConfGlobalServer.ServerStartTime) < gConfGlobal.PayCarnivalOpenDays 
            || this.user.status.level < gConfGlobal.PayCarnivalOpenLevel) {
            return false;
        }

        var payCarnival = this.user.pay_carnival;
        if (!payCarnival || (payCarnival.reach_time && 
        (nowDate.getDays() >= Date.createFromStamp(payCarnival.reach_time).getDays() + gConfGlobal.PayCarnivalAwardDays))) {
            this.user.pay_carnival = {
                'total_cash': 0,
                'reach_time': 0,
                'award_step': 0,
            };

            payCarnival = this.user.pay_carnival;
            for (var attr in payCarnival) {
                this.markDirty('pay_carnival.'+attr);
            }
        }
        return true;
    },

    // 增加值嘉年华的累计充值
    addPayCarnivalCash : function(cash) {
        if (!this.checkPayCarnival()) {
            return;
        }

        var payCarnival = this.user.pay_carnival;
        payCarnival.total_cash += cash;
        if (!payCarnival.reach_time && payCarnival.total_cash >= gConfGlobal.PayCarnivalTotalCash) {
            payCarnival.reach_time = common.getTime();
            payCarnival.award_step = 1;
            this.markDirty('pay_carnival.reach_time');
            this.markDirty('pay_carnival.award_step');
        }
        this.markDirty('pay_carnival.total_cash');
    },

    getDailyVar : function(key) {
        return this.user.daily_vars[key] || 0;
    },

    getDailyVars :function() {
        var objVars = {};
        var dailyVars = this.user.daily_vars;
        for (var i = 0; i < arguments.length; ++i) {
            var key = arguments[i];
            objVars[key] = dailyVars[key] || 0;
        }
        return objVars;
    },

    addDailyVar : function(key, value) {
        var newValue = addAttrValue(this.user.daily_vars, key, value);        
        this.markDirty('daily_vars.' + key);
        return newValue;
    },

    setDailyVar : function(key, value) {
        this.user.daily_vars[key] = value;
        this.markDirty('daily_vars.' + key);
    },

    checkDailyVars : function() {
        var nowDay = (new Date()).getDays();
        if (!this.user.daily_vars) {
            this.user.daily_vars = {'day':nowDay};
            this.markDirty('daily_vars.day');
            this.mergeDirty();
            return;
        }

        var vars = this.user.daily_vars;
        if (nowDay != vars.day) {
            for (var key in vars) {
                vars[key] = 0;
                this.markDirty('daily_vars.' + key);
            }
            vars.day = nowDay;
            this.markDirty('daily_vars.day');
            this.doEventNewDay();
            this.mergeDirty();
        }
    },

    getHuaTuoLevelConf: function() {
        var xp = this.user.huatuo.xp;
        for( i=1; i<1000; i++ ) {
            if( !gConfHuaTuoLevel[i+1] || gConfHuaTuoLevel[i].Xp >= xp ) {
                break;
            }
        }

        return gConfHuaTuoLevel[i];
    },

    giveTaskV3Reward: function(step) {
        var activity = this.user.activity;
        var taskConf = gConfQQTask[activity.app_contract_id];
        if (!taskConf) {
            return;
        }

        stepConf = taskConf[step];
        if (!stepConf || !stepConf.Awards) {
            return;
        }
        
        this.addStatusAwards(this.addAwards(stepConf.Awards));
        this.addStatusNotice(util.format('恭喜您获得 %s', stepConf.Desc));

        // 发放任务集市V3奖励
        this.user.activity.contract_task_reward_v3 = step;
        this.markDirty('activity.contract_task_reward_v3');
    },

    checkTaskV3Step: function(step) {
        var activity = this.user.activity;
        if (!gConfSwitch.QQTask || !activity.app_contract_id) {
            return false;
        }

        if (activity.contract_task_v3 >= step) {
            return true;
        }

        var taskConf = gConfQQTask[activity.app_contract_id];
        if (!taskConf) {
            return false;
        }

        var goalConf = null;
        for (;;) {
            goalConf = taskConf[activity.contract_task_v3 + 1];
            if (!goalConf) {
                break;
            }

            if (!goalConf.GoalEvent) {
                activity.contract_task_v3++;
                this.markDirty('activity.contract_task_v3');
                continue;
            }

            var eventClass = questEvent[goalConf.GoalEvent];
            var value = eventClass.getValue(this.user, 0, goalConf);
            if (value < goalConf.GoalTarget) {
                break;
            }

            activity.contract_task_v3++;
            this.markDirty('activity.contract_task_v3');
        }

        return (activity.contract_task_v3 >= step);
    },

    getTroopMatch: function(check) {
        var troopMatch = this.user.troop_match;

        if (!troopMatch) {
            var week = Math.ceil((Date.getStamp() - gConfRuntime.TroopMatchStartTime) / 86400 / 7);
            troopMatch = {
                'max_win' : 0,          // 最大连胜
                'cur_win' : 0,          // 当前连胜
                'lose' : 0,             // 失败次数
                'score' : 0,            // 积分
                'time' : 0,             // 上次进入时间
                'clear_time':0,         // 今日已经清除time次数        
                'roles' : [],           // 获取的武将
                'options' : [],         // 可选武将
                'team' : {},            // 阵形
                'week' : week,          // 周
                'week_score' : 0,       // 周积分
            };

            this.user.troop_match = troopMatch;
            for (var key in troopMatch) {
                this.markDirty('troop_match.' + key);
            }
        } else if (check) {
            var week = Math.ceil((Date.getStamp() - gConfRuntime.TroopMatchStartTime) / 86400 / 7);
            if (week != troopMatch.week) {
                troopMatch.week = week;
                troopMatch.week_score = 0;
                this.markDirty('troop_match.week');
                this.markDirty('troop_match.week_score');
            }
        }

        return  troopMatch;
    },

    // 结束战术大赛
    troopMatchOver : function() {
        var troopMatch = this.getTroopMatch();

        troopMatch.time = common.getTime();
        troopMatch.roles = [];
        troopMatch.options = [];
        troopMatch.team = {};
        troopMatch.lose = 0;
        troopMatch.cur_win = 0;
        troopMatch.win_streak = 0;

        this.markDirty('troop_match.time')
        this.markDirty('troop_match.roles')
        this.markDirty('troop_match.options')
        this.markDirty('troop_match.team')
        this.markDirty('troop_match.lose')
        this.markDirty('troop_match.cur_win')
        this.markDirty('troop_match.win_streak')
    },

    troopMatchFight : function(battleReport, resp) {
        var troopMatch = this.getTroopMatch();

        if( battleReport.success ) {
            troopMatch.cur_win += 1;
            this.markDirty('troop_match.cur_win')

            addAttrValue(troopMatch, 'win_streak', 1);
            this.markDirty('troop_match.win_streak')

            if( troopMatch.cur_win > troopMatch.max_win ) {
                troopMatch.max_win = troopMatch.cur_win;
                this.markDirty('troop_match.max_win')
            }

            var troopMatchScoreConf = gConfTroopMatchScore[troopMatch.cur_win];
            var winStreakConf = gConfTroopMatchWinStreak[troopMatch.win_streak];

            var addScore = troopMatchScoreConf.Score;
            troopMatch.score += addScore;
            troopMatch.week_score += addScore;
            this.markDirty('troop_match.score')
            this.markDirty('troop_match.week_score')

            var awards = [];

            var award1 = troopMatchScoreConf['Award1'];
            if (award1) {
                awards.push(award1);
            }

            var award2 = troopMatchScoreConf['Award2'];
            if (award2 && !troopMatch.lose) {
                awards.push(award2);
            }

            for (var i = 1; i <= 1; ++i) {
                var award = winStreakConf['Award'+i];
                if (award) {
                    awards.push(award);
                }
            }

            resp.data.awards = this.addAwards(awards);
            if (!troopMatch.lose) {
                this.checkEvent('DoTroopMatchWin', troopMatch.win_streak);
            }
        }else{
            troopMatch.lose += 1;
            troopMatch.win_streak = 0;
            this.markDirty('troop_match.lose')
            this.markDirty('troop_match.win_streak')
        }

        if( troopMatch.lose >= 3 || troopMatch.cur_win >= gConfGlobal.TroopMatchWinLimit ) {
            // Game Over
            this.troopMatchOver();
        }
    },

    checkTutorialLevelPackAwards : function() {
        var awards = [];
        var userLevel = this.user.status.level;
        var levelPackAward = this.user.mark.level_pack_award;
        if( !(userLevel in gConfLevelPack) || levelPackAward >= userLevel ) {
            return awards;
        }
        
        var levelPackConf = gConfLevelPack[userLevel];
        for( var i = 1; i <= 10; i++ ) {
            var award = levelPackConf['Award'+i]; 
            if( !award ) continue;
            awards.push(award); 
        }
        
        this.user.mark.level_pack_award = userLevel;
        this.markDirty('mark.level_pack_award');
        return this.addAwards(awards);
    },

    addHorse : function(horseId) {
        var horseUniqId = this.nextId();

        var horse = {
            'horse_id' : horseId,
            'level' : 1,
        };

        this.user.ride.horse[horseUniqId] = horse;
        this.markDirty(util.format('ride.horse.%d', horseUniqId));
        this.doEventAddHorse(horse);

        return horseUniqId;
    },

    addRoleRide : function(roleId, horseUniqId) {
        var user = this.user;
        if( !user.role[roleId] || !user.ride.horse[horseUniqId] ) {
            return null; 
        }

        var ride = {
            'horse' : +horseUniqId, 
            'seat'  : 1, 
            'whip'  : 1,
        }; 

        user.role[roleId].ride = ride; 
        this.markDirty(util.format('role.%d.ride', roleId));

        return ride; 
    },

    addRoleWing : function(roleId, initialLevel) {
        var role = this.user.role[roleId]; 
        if( !role || !role.god || role.wing ) return;

        role.wing = +initialLevel;
        this.markDirty(util.format('role.%d.wing',roleId));
    },

    getEquipRoleId: function(eid) {
        if (!eid) {
            return 0;
        }

        var roles = this.user.role;
        for (var rid in roles) {
            var role = roles[rid];
            if (eid == role.weapon || eid == role.armor || eid == role.accessory) {
                return rid;
            }
        }
        return 0;
    },
    
    // 获得战斗力最高的武将镜像
    roleImageInit: function() {
        if (this.user.role_images) {
            return;
        }

        this.user.role_images = {};
        this.markDirty('role_images');
        this.calculateTeamRoleAttr(true);
    },

    getRoleImages: function() {
        var user = this.user;
        var roleImages = this.user.role_images;
        if (!roleImages) {
            this.roleImageInit();
            roleImages = this.user.role_images;
        } else {
            for (var rid in roleImages) {
                var roleImage = roleImages[rid];
                if (!roleImage.hasOwnProperty('star')) {
                    this.updateRoleImages(true);
                }
                break;
            }
        }
        return roleImages;
    },

    getRoleImageAttrCount: function(attr) {
        var roleImages = this.user.role_images;
        if (!roleImages) {
            return 0;
        }

        var count = 0;
        for (var roleId in roleImages) {
            count += (roleImages[roleId][attr]||0);
        }

        return count;
    },

    getRoleImageFightForce: function() {
        return this.getRoleImageAttrCount('fight_force');
    },

    updateRoleImages: function(force) {
        var roles = this.user.role;
        var roleArr = [];
        for (var rid in roles) {
            var role = roles[rid];
            roleArr.push(role);
        }

        roleArr.sort(function(a, b) {
            return b.fight_force - a.fight_force;
        });
        
        var topRoleCount = Math.min(roleArr.length, 7);
        var topFightForce = 0;
        for (var i = 0; i < topRoleCount; ++i) {
            topFightForce += roleArr[i].fight_force;
        }

        if (force || topFightForce > this.getRoleImageFightForce()) {
            var roleImages = {};
            for (var i = 0; i < topRoleCount; ++i) {
                var role = roleArr[i];
                var roleAttr = clone(this.roleAttrs[role.id]);
                roleAttr.fight_force = role.fight_force;
                roleAttr.god = +role.god || 0;
                roleAttr.star = role.star || 0;
                roleImages[role.id] = roleAttr;
            }
            this.user.role_images = roleImages;
            this.markDirty('role_images');
        }
    },

    // 横扫九州
    smashLandInit: function() {
        if (this.user.smashland) {
            return;
        }

        this.roleImageInit();
        this.user.smashland = {buffs:{}, coin:0};
        this.markDirty('smashland.buffs');
        this.markDirty('smashland.coin');
        this.smashLandReset();
    },

    smashLandReset: function() {
        var smashland = this.user.smashland;
        if (!smashland) {
            return;
        }

        var attrs = {};
        var roleAttrs = this.roleAttrs;
        for (var rid in roleAttrs) {
            var attr = roleAttrs[rid];
            attrs[rid] = {
                init_health: attr.health, 
                health: attr.health, 
                morale: 200
            };
        }
        
        var team = this.user.team;
        smashland.team = {};
        for (var pos in team) {
            var rid = team[pos];
            if (rid) {
                smashland.team[pos] = rid;
            }
        }
        smashland.attrs = attrs;
        smashland.buffs = {};
        smashland.score = 0;

        if (smashland.cloud) {
            smashland.cloud.todayScore = 0;
            this.markDirty('smashland.cloud');
        }
    
        this.markDirty('smashland.team');
        this.markDirty('smashland.attrs');
        this.markDirty('smashland.buffs');
        this.markDirty('smashland.score');
        this.smashLandSetId(1);
    },
    
    smashLandSetId: function(id) {
        var smashland = this.user.smashland;
        smashland.enemy_team = {};
        smashland.enemy_attr = {};
        smashland.cur_id = id;
        smashland.cur_level = 0;

        if (id) {
            var smashConf = gConfSmashLand[id];
            for (var level in smashConf) {
                smashland.enemy_team[level] = common.randArray(smashConf[level].Teams);
            }
        }

        this.markDirty('smashland.enemy_team');
        this.markDirty('smashland.enemy_attr');
        this.markDirty('smashland.cur_id');
        this.markDirty('smashland.cur_level');
    },

    smashLandSetLevel: function(level, factor) {
        var smashland = this.user.smashland;
        var health = Math.floor(this.getRoleImageAttrCount('health') * factor);

        smashland.cur_level = level;
        smashland.enemy_attr = {'init_health':health, 'health':health};

        this.markDirty('smashland.enemy_attr');
        this.markDirty('smashland.cur_level');
    },
    
    
    doEventRoleAwake : function(role) {
        this.checkEvent('HaveGodRole', role.god);

        var activityKey = 'godgift';
        var activityInfo = this.getActivity(activityKey, {conf:true});
        if (activityInfo && activityInfo.data) {
            var avConf = gConfAvGodGift[activityInfo.conf.conf];
            if (role.god == avConf.GodLevel) {
                var activity = activityInfo.data;
                addAttrValue(activity, 'times_count', 1);
                addAttrValue(activity, 'times_award', 1);
                this.saveActivityValue(activityKey, 'times_count');
                this.saveActivityValue(activityKey, 'times_award');

                this.addWorldActivityValue(activityKey, activityInfo.conf.time, 'server_count', 1);
            }
        }
    },

    doEventRoleSoul : function(role) {
        this.doTask('UpgradeSoulLevel','all');
        this.checkEvent('RoleSoulSum');
    },

    doEventRoleRefine : function(role) {
        this.doTask('RefineRole','all');
        this.checkEvent('DoRoleRefine');
        this.checkEvent('RoleRefineSum');
    },

    doEventRoleUpgradeWing : function(role) {
        this.checkEvent('HaveWingRole', role.wing);
    },

    doEventRoleUpgradeStar : function(role) {
        this.checkEvent('HaveStarRole', role.star);
    },

    doEventRoleUpgradeGrouth : function(role) {
        this.doTask('UpgradeGrouth','all');
        this.checkEvent('RoleGrouthSum');
    },

    doEventRoleUpgradeSkill : function(role, skillId) {
        this.doTask('UpgradeSkill','all');
        this.checkEvent('RoleSkillSum', role.skill_levels[skillId]);
    },

    doEventAddRole : function(role) {
        this.checkEvent('FightForce');
        this.checkEvent('RoleGrouthSum');
        this.checkEvent('HaveRole');
    },
    
    doEventAddHorse : function(horse) {
        var horseConf = gConfHorse[horse.horse_id];
        this.checkEvent('HaveHorse', horseConf.Quantity);
    },
    
    doEventEquipStrength : function(equip) {
        this.doTask('StrengthEquip', 'all');
        var roleId = this.getEquipRoleId(equip.id);
        if (roleId) {
            this.checkEvent('RoleEquipStrengthSum', roleId);
        }
    },
    
    doEventEquipMake : function(equip) {
        this.doTask('MakeEquip','all');
        this.checkEvent('HaveLevelEquip', equip.level);
        var roleId = this.getEquipRoleId(equip.id);
        if (roleId) {
            this.checkEvent('RoleEquipLevelSum', roleId);
        }
    },

    doEventEquipUpgradeStar : function(equip) {
        this.checkEvent('HaveStarEquip', equip.star);
    },

    doEventNewDay: function() {
        this.smashLandReset();

        var troopMatch = this.getTroopMatch();
        troopMatch.clear_time = 0;
        this.markDirty('troop_match.clear_time');

        var xzr = this.user.xingzuo_romance;
        if (xzr) {
            xzr.high = 0;
            xzr.normal = 0;
            this.markDirty("xingzuo_romance");
        }
    },

    requestCloudRank: function(query, resp, callback) {
        var user = this.user;
        query.uid = this.uid;
        if (!query.args) {
            query.args = {};
        }
        query.args.user = {
            'info': {
                un: user.info.un,
                headpic: user.info.headpic,
            },

            'status': {
                level: user.status.level,
                vip: user.status.vip,
            },
        }
        requestCloudRank(query, resp, function() {
            delete query.args.user;
            callback && callback();
        });
    },

    requestChampsBattle: function(query, resp, callback) {
        var user = this.user;
        query.uid = this.uid;
        if (!query.args) {
            query.args = {};
        }
                
        var gradeLevel = 1;
        if (user.league_war) {
        	gradeLevel = user.league_war.grade_level;
        }

        query.args.user = {
            'info': {
                un: user.info.un,
                headpic: user.info.headpic,
            },

            'status': {
                level: user.status.level,
                vip: user.status.vip,
                gradeLevel: gradeLevel,
            },
        }

        requestChampsBattle(query, resp, function() {
            delete query.args.user;
            callback && callback();
        });
    },

    getChampsBattle: function() {
        var round = gConfRuntime.champsbattle.getRound();
        var champsbattle = this.user.champsbattle;

        if (!champsbattle || champsbattle.round != round) {
            champsbattle = {
                round: round,
                enterable: true,
                awardStep: 1,
                finalAward: 0,
                support: {},
            }
            this.user.champsbattle = champsbattle;

            for (attr in champsbattle) {
                this.markDirty('champsbattle.' + attr);
            }
        }

        return champsbattle;
    },

    // 好友系统
    addFriendPoint: function(point) {
        if (point > 0) {
            var dayGetPoint = this.getDailyVar('friend_point');
            var maxDayGetPoint = +gConfGlobal.NewFriendMaxDailyGetFriendPoint;
            if (dayGetPoint >= maxDayGetPoint) {
                return this.user.status.friend_point;
            }
            this.setDailyVar('friend_point', point + dayGetPoint);
        }
        var newPoint = Math.max(0, (+(this.user.status.friend_point || 0) + point));
        this.user.status.friend_point = newPoint;
        this.markDirty('status.friend_point');
        return newPoint;
    },

    /**
     * 今天的好友点数是否达到上限
     */
    isFriendPointFull: function() {
        var dayGetPoint = this.getDailyVar('friend_point');
        var maxDayGetPoint = +gConfGlobal.NewFriendMaxDailyGetFriendPoint;
        return dayGetPoint >= maxDayGetPoint;
    },

    getMaChaoBox: function() {
        if (this.user.machao_box && this.user.machao_box.id != -1) {
            return this.user.machao_box;
        }

        this.user.machao_box = {
            'id': 1,
            'progress': 0
        };
        this.markDirty("machao_box.id");
        this.markDirty("machao_box.progress");
        return this.user.machao_box;
    },

    // 平乱事件
    triggerInsurgency: function(){
        // 检测开启等级
        if (this.user.status.level < +gConfGlobal.InsurgencyLevelLimit) {
            return;
        }
        // 计算是否能发生平乱事件
        var randNum = Math.floor(Math.random() * 100);
        if (randNum <= +gConfGlobal.InsurgencyTriggerChance) { // 触发事件
            gInsurgency.triggerEvent(this, this.uid);
        }
    },

    // 武将天赐培养
    getRoleGrouthGive: function() {
        var grouthGive = this.user.role_grouth_give;
        if (!grouthGive) {
            grouthGive = {};
            this.user.role_grouth_give = grouthGive;
            this.markDirty("role_grouth_give");
        }
        return grouthGive;
    },

    //获取星魂农场信息
    getHoroscopeFarm: function() {
        var horoscopeFarm = this.user.horoscope_farm;
        if (!horoscopeFarm && this.user.status.level >= gConfGlobal.HoroscopeFarmOpenLevel) {
            horoscopeFarm = {
                score: 0,
                store: [],
                land: [],
            }
            this.user.horoscope_farm = horoscopeFarm;

            this.refreshHoroscopeFarmStore();
            objectForEach(gConfHoroscopeLand, function(id, land) {
                if (!land.Costs) {
                    horoscopeFarm.land.push({seed:0, stamp:0, water:0});
                }
            });

            for (var key in horoscopeFarm) {
                this.markDirty("horoscope_farm." + key);
            }
        }

        return horoscopeFarm;
    },
    
    //刷新星魂农场种子商店
    refreshHoroscopeFarmStore: function() {
        var horoscopeFarm = this.user.horoscope_farm;
        if (!horoscopeFarm) {
            return;
        }

        var store = [];
        var weights = gConfRuntime.HoroscopeFarmSeedWeights;
        for (var i = 0; i < gConfRuntime.HoroscopeFarmStoreSeedCount; ++i) {
            store.push({seed:+common.wRand(weights), status:0});
        }

        horoscopeFarm.store = store;
        this.markDirty("horoscope_farm.store");
    },
}

function checkNonCountingTask(player, taskType, taskArg, target) {
    var user = player.user;
    var isFinished = true;
    
    switch(taskType) {
        case 'LevelUp':
            if( user.status.level < target ){
                isFinished = false; 
            }   
            break;
        case 'HaveGold':
            if( user.status.gold < target ) {
                isFinished = false; 
            }
            break;
        case 'HaveHonor':
            if( user.status.honor < target ) {
                isFinished = false; 
            }
            break;
        case 'HaveStrengthEquip':
            var num = 0;
            for( var id in user.equip ) {
                if( taskArg <= user.equip[id].strength ) {
                    num += 1;
                }
            }

            if( num < target ) {
                isFinished = false; 
            }   
            break;
        case 'HaveGrouthRole':
            var num = 0;
            for( var id in user.role ) {
                if( taskArg <= user.role[id].grouth ) {
                    num += 1;
                }
            }

            if( num < target ) {
                isFinished = false; 
            }   
            break;
        case 'HaveMakeEquip':
            var num = 0;
            for( var id in user.equip ) {
                if( taskArg <= user.equip[id].level ) {
                    num += 1;
                }
            }

            if( num < target ) {
                isFinished = false; 
            }
            break;
        case 'HaveMaterial': 
            if( player.getInventory('material', taskArg) < target ) {
                isFinished = false; 
            }
            break;
        case 'UpgradeFightForceTo':
            if( player.getFightForce() < target ) {
                isFinished = false; 
            }
            break;
        case 'HaveRole':
            var num = 0;
            for( var id in user.role ) {
                if( taskArg == gConfRole[id].Quantity ) {
                    num += 1; 
                } 
            }

            if( num < target ) {
                isFinished = false; 
            }  
            break;
        case 'UpgradeTechTo':
            var num = 0;
            var tech = user.tech;
            for( var id in tech ) {
                if( tech[id] >= taskArg ) {
                    num += 1; 
                } 
            }

            if( num < target ) {
                isFinished = false; 
            }
            break;
        case 'HaveArmyRole':
            var num = 0;
            var roles = user.role;
            for( var id in roles ) {
                if( roles[id].soldier_level >= taskArg ) {
                    num += 1; 
                } 
            }

            if( num < target ) {
                isFinished = false; 
            }
            break;
        case 'FightTowerTo':
            if( user.tower.highest[1] < target ) {
                isFinished = false; 
            }
            break;

        case 'KillGeneralTo':
            if( user.kill.progress <= taskArg) {
                isFinished = false; 
            }
            break;
        default :
            isFinished = false;
            break;
    }
     
    return isFinished;
}

function PlayerManager(){
    this.players = {};
    this.actions = {};
    this.loadings = {};
    this.unloadings = {};
}

PlayerManager.prototype = {
    get : function(uid, callback) {
        var player = this.players[uid];
        if (player) {
            callback && callback(player);
        }else{
            this.load(uid, callback);
        }
    },

    load : function(uid, callback) {
        if (this.loadings[uid] || this.unloadings[uid]) {
            callback && callback(null);
            return;
        }

        this.loadings[uid] = true;
        var player = this.players[uid];
        if (player) {
            if (player.isSaving) {
                delete this.loadings[uid];
                callback && callback(player);
            } else {
                player.save(true, function() {
                    if (player.lock) {
                        this._load(uid, callback);
                    } else {
                        delete this.loadings[uid];
                        callback && callback(player);
                    }
                }.bind(this));
            }
        } else {
            this._load(uid, callback);
        }
    },

    _load : function(uid, callback) {
        var player = new Player(uid);
        player.init({}, function(succ){
            delete this.loadings[uid];
            if (succ) {
                this.players[player.uid] = player;
                callback && callback(player);
            } else {
                callback && callback(null);
            }
        }.bind(this));
    },

    unload : function(uid){
        var player = this.players[uid];
        if (!player || !player.user) {
            return;
        }

        if (player.loginStamp) {
            var user = player.user;
            var logoutStamp = Math.max(player.loginStamp, player.lastActive);
            var onlineTime = Math.max(0, logoutStamp - player.loginStamp);
            player.addDailyVar('onlineTime', onlineTime);
            addAttrValue(user.mark, 'online_time', onlineTime);
            player.markDirty('mark.online_time');
            writeAudit(util.format('Login %d %d %d %d %s', player.uid, player.loginStamp, logoutStamp, user.status.level, player.clientIP));

            //RoleLogout|iEventId|iWorldId|iUin|dtEventTime|dtLoginTime|vClientIp|
            //vZoneId|dtCreateTime|iOnlineTime|iOnlineTotalTime|iRoleId|vRoleName|
            //iRoleLevel|iRoleExp|iRepute|iVipLevel|iMoney|iAccumDeposit|iAccumConsume|iGameTime|iLoginWay
            writeRecord(util.format('RoleLogout|kick|%d|%s|%s|%s|%s|0|%s|%d|%d|%d|RoleName|%d|%d|%d|%d|%d|0|0|0|0', 
                config.ServerId, player.openid, Date.stdFormatedString(), Date.createFromStamp(player.loginStamp).stdFormatedString(), player.clientIP, 
                Date.createFromStamp(user.info.create).stdFormatedString(), onlineTime, user.mark.online_time, player.uid, 
                user.status.level, user.status.xp, user.status.fight_force, user.status.vip, user.status.cash));
        }
        
        delete this.players[uid];
        this.unloadings[uid] = true;
        player.save(true, function() {
            delete this.unloadings[uid];
        }.bind(this));
    },

    onlineCount : function() {
        var count = 0;
        var now = Date.getStamp();
        for (var uid in this.players) {
            var player = this.players[uid];
            if ((now - player.lastActive) < 300) {
                count++; 
            }
        }
        return count;
    },

    kick : function() {
        var offlines = [];
        var now = common.getTime();

        var m = arguments.callee;
        if (!m.times) {
            m.times = 0;
        }
        m.times++;
        
        var onlineCount = 0;
        for( var uid in this.players ) {
            var player = this.players[uid];
            var duration = now - player.lastActive;
            if (duration < 300) {
                onlineCount++;
            } else if (duration > 1800) {
               offlines.push(uid); 
            }
        }

        for(var i=0,max=offlines.length; i<max; i++) {
            this.unload(offlines[i]);
        }

        if (m.times%5 == 0) {
            //OnlineCount|iEventId|dtEventTime|iWorldId|iAccountCount|iAccountMax|iPlayerCount|iPlayerMax
            writeRecord(util.format('OnlineCount|kick|%s|%d|%d|%d|%d|%d',
                Date.stdFormatedString(), config.ServerId, onlineCount, onlineCount, onlineCount, onlineCount));
        }
    },

    actionAnalysis : function(uid) {
        if( !this.actions[uid] ) {
            this.actions[uid] = {
                'min':0, 
                'mins':0,
                'time':0,
            };
        }

        var action = this.actions[uid];

        var sec = common.getTime();
        var min = Math.ceil(sec/300);

        if( min != action.min ) {
            action.min = min;
            action.mins = 1;
        }else{ 
            action.mins += 1;
        }

        if( action.mins >= 500 ) {
            action.time = sec + 3600;
            ERROR('block '+ uid);
        }
    },

    isActionBlock : function(uid) {
        var action = this.actions[uid];
        if( !action ) return false;
        return action.time > common.getTime();
    },

    addBlock : function(uid) {
        if( this.isActionBlock(uid) ) return;

        if( !this.actions[uid] ) {
            this.actions[uid] = {
                'min':0, 
                'mins':0,
                'time':0,
            };
        }
        var action = this.actions[uid];
        action.time = common.getTime() + 36000;
        ERROR('block by seq '+ uid);
    },
};

exports.Player = Player;
exports.PlayerManager = PlayerManager;
